v0.25.1
This commit is contained in:
1 parent
87e571c170
commit
fa7abc9e53
308 files changed
+49746
-50237
No files matched your search
+126
-82
@@ -21,11 +21,19 @@ import backup as backup_module
|
||||
import deploy
|
||||
import migrate_npm
|
||||
import upgrade as upgrade_module
|
||||
from rust_source import rust_source
|
||||
from web_assets import read_app_js, read_vhosts_js
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
def read_cache_source() -> str:
|
||||
return "\n".join(path.read_text(encoding="utf-8") for path in sorted((ROOT / "src/cache").glob("*.rs")))
|
||||
return rust_source(ROOT, "src/cache/mod.rs")
|
||||
|
||||
def read_deploy_source() -> str:
|
||||
return "\n".join((ROOT / name).read_text(encoding="utf-8") for name in ("scripts/deploy_core.py", "scripts/deploy_runtime.py", "scripts/deploy.py"))
|
||||
|
||||
def read_docker_agent_source() -> str:
|
||||
return "\n".join(path.read_text(encoding="utf-8") for path in sorted((ROOT / "docker-agent").glob("*.go")) if not path.name.endswith("_test.go"))
|
||||
|
||||
|
||||
def sql_and_backup_smoke(tmp: Path) -> None:
|
||||
@@ -114,13 +122,18 @@ def upgrade_smoke(tmp: Path) -> None:
|
||||
assert backup.is_dir()
|
||||
assert (backup / "src/obsolete.rs").is_file()
|
||||
assert not (target / "src/obsolete.rs").exists()
|
||||
assert 'version = "0.25.0"' in (target / "Cargo.toml").read_text(encoding="utf-8")
|
||||
assert 'version = "0.25.1"' in (target / "Cargo.toml").read_text(encoding="utf-8")
|
||||
assert (target / "data/keep.txt").read_text(encoding="utf-8") == "preserve\n"
|
||||
assert (target / ".env").read_text(encoding="utf-8") == "RUST_PROXY_DEMO=0\n"
|
||||
assert (target / "langs/site.json").is_file()
|
||||
assert (target / "docker-agent/main.go").is_file()
|
||||
assert (target / "docker-agent/model.go").is_file()
|
||||
assert (target / "docker-agent/docker.go").is_file()
|
||||
assert (target / "docker-agent/network.go").is_file()
|
||||
assert (target / "docker-agent/certificate.go").is_file()
|
||||
assert (target / "docker-agent/server.go").is_file()
|
||||
manifest = json.loads((backup / "upgrade.json").read_text(encoding="utf-8"))
|
||||
assert manifest["to_version"] == "0.25.0"
|
||||
assert manifest["to_version"] == "0.25.1"
|
||||
assert manifest["removed_obsolete_runtime_state"] is True
|
||||
assert not legacy.exists()
|
||||
assert not (backup / "legacy-data").exists()
|
||||
@@ -162,7 +175,7 @@ def deployment_gate_smoke(tmp: Path) -> None:
|
||||
assert "TimeoutStopSec=120" in remote_unit
|
||||
for marker in ["PrivateDevices=true", "ProtectKernelLogs=true", "RestrictNamespaces=true", "RestrictRealtime=true", "SystemCallArchitectures=native"]:
|
||||
assert marker in remote_unit, marker
|
||||
deploy_source = (ROOT / "scripts/deploy.py").read_text(encoding="utf-8")
|
||||
deploy_source = read_deploy_source()
|
||||
assert "def stop_for_managed_update" in deploy_source and ".managed-update" in deploy_source
|
||||
assert "systemctl', 'stop', SERVICE" in deploy_source
|
||||
assert "def ensure_service_data_access" in deploy_source
|
||||
@@ -234,9 +247,9 @@ def npm_migration_smoke(tmp: Path) -> None:
|
||||
|
||||
def ui_contract_smoke() -> None:
|
||||
html = (ROOT / "src/admin/web/index.html").read_text(encoding="utf-8")
|
||||
app = (ROOT / "src/admin/web/app.js").read_text(encoding="utf-8")
|
||||
app = read_app_js(ROOT)
|
||||
settings = (ROOT / "src/admin/web/settings.js").read_text(encoding="utf-8")
|
||||
vhosts = (ROOT / "src/admin/web/vhosts.js").read_text(encoding="utf-8")
|
||||
vhosts = read_vhosts_js(ROOT)
|
||||
streams = (ROOT / "src/admin/web/streams.js").read_text(encoding="utf-8")
|
||||
redirects = (ROOT / "src/admin/web/redirects.js").read_text(encoding="utf-8")
|
||||
host404 = (ROOT / "src/admin/web/host404.js").read_text(encoding="utf-8")
|
||||
@@ -251,7 +264,15 @@ def ui_contract_smoke() -> None:
|
||||
'id="traffic-scope"',
|
||||
'id="traffic-chart-countries"',
|
||||
'id="traffic-chart-client-ips"',
|
||||
'id="traffic-client-ips"',
|
||||
'id="traffic-tabs"',
|
||||
'data-traffic-tab="overview"',
|
||||
'data-traffic-tab="clients"',
|
||||
'data-traffic-tab="requests"',
|
||||
'data-traffic-tab="delivery"',
|
||||
'id="traffic-chart-user-agents"',
|
||||
'id="traffic-chart-paths"',
|
||||
'id="traffic-chart-routes"',
|
||||
'id="traffic-chart-volume"',
|
||||
'id="log-capture-user-agent"',
|
||||
'id="log-capture-country"',
|
||||
'id="cache-auto-trim-enabled"',
|
||||
@@ -316,6 +337,8 @@ def ui_contract_smoke() -> None:
|
||||
for marker in required_html:
|
||||
assert marker in html, marker
|
||||
assert '/api/traffic' in traffic and 'rustProxyTrafficPanel' in traffic
|
||||
for marker in ['function selectTab(', 'function renderDonut(', 'function renderBars(', 'renderBars("client-ips"', 'renderBars("user-agents"', 'renderBars("paths"', 'renderBars("routes"', 'renderBars("volume"']:
|
||||
assert marker in traffic, marker
|
||||
assert 'traffic: "/traffic"' in app and 'window.rustProxyTrafficPanel?.refresh()' in app
|
||||
for marker in ['id="settings-tab-manual-config"', 'id="settings-page-manual-config"', 'id="config-editor"', 'id="save-json"', 'id="export-config"']:
|
||||
assert marker in html, marker
|
||||
@@ -439,13 +462,13 @@ def ui_contract_smoke() -> None:
|
||||
|
||||
|
||||
def source_contract_smoke() -> None:
|
||||
config = (ROOT / "src/config.rs").read_text(encoding="utf-8")
|
||||
tls = (ROOT / "src/tls.rs").read_text(encoding="utf-8")
|
||||
main = (ROOT / "src/main.rs").read_text(encoding="utf-8")
|
||||
server = (ROOT / "src/server.rs").read_text(encoding="utf-8")
|
||||
logging = (ROOT / "src/logging.rs").read_text(encoding="utf-8")
|
||||
secret_store = (ROOT / "src/secret_store.rs").read_text(encoding="utf-8")
|
||||
backup = (ROOT / "src/backup.rs").read_text(encoding="utf-8")
|
||||
config = rust_source(ROOT, "src/config.rs")
|
||||
tls = rust_source(ROOT, "src/tls.rs")
|
||||
main = rust_source(ROOT, "src/main.rs")
|
||||
server = rust_source(ROOT, "src/server.rs")
|
||||
logging = rust_source(ROOT, "src/logging.rs")
|
||||
secret_store = rust_source(ROOT, "src/secret_store.rs")
|
||||
backup = rust_source(ROOT, "src/backup.rs")
|
||||
acme_worker = (ROOT / "scripts/acme_provider.py").read_text(encoding="utf-8")
|
||||
for feature_marker in ["AES_256_GCM", "master.key", "O_NOFOLLOW", "migrate_acme_credentials"]:
|
||||
assert feature_marker in secret_store, feature_marker
|
||||
@@ -455,36 +478,36 @@ def source_contract_smoke() -> None:
|
||||
assert feature_marker in logging, feature_marker
|
||||
assert "credential_ini_b64" in acme_worker and "credential_file.unlink" in acme_worker
|
||||
cache = read_cache_source()
|
||||
settings = (ROOT / "src/settings.rs").read_text(encoding="utf-8")
|
||||
websocket = (ROOT / "src/proxy/websocket.rs").read_text(encoding="utf-8")
|
||||
proxy = (ROOT / "src/proxy/mod.rs").read_text(encoding="utf-8")
|
||||
settings = rust_source(ROOT, "src/settings.rs")
|
||||
websocket = rust_source(ROOT, "src/proxy/websocket.rs")
|
||||
proxy = rust_source(ROOT, "src/proxy/mod.rs")
|
||||
assert '.insert("x-rustproxy-backend-time", value)' in proxy and '.insert("x-rust-proxy-backend-time", value)' not in proxy
|
||||
pages = (ROOT / "src/pages.rs").read_text(encoding="utf-8")
|
||||
admin = (ROOT / "src/admin/mod.rs").read_text(encoding="utf-8")
|
||||
stream = (ROOT / "src/stream.rs").read_text(encoding="utf-8")
|
||||
runtime = (ROOT / "src/runtime.rs").read_text(encoding="utf-8")
|
||||
acl = (ROOT / "src/acl.rs").read_text(encoding="utf-8")
|
||||
pages = rust_source(ROOT, "src/pages.rs")
|
||||
admin = rust_source(ROOT, "src/admin/mod.rs")
|
||||
stream = rust_source(ROOT, "src/stream.rs")
|
||||
runtime = rust_source(ROOT, "src/runtime.rs")
|
||||
acl = rust_source(ROOT, "src/acl.rs")
|
||||
policies = (ROOT / "tests/policies.rs").read_text(encoding="utf-8")
|
||||
http3 = (ROOT / "src/http3.rs").read_text(encoding="utf-8")
|
||||
redirect = (ROOT / "src/redirect.rs").read_text(encoding="utf-8")
|
||||
not_found = (ROOT / "src/not_found.rs").read_text(encoding="utf-8")
|
||||
prometheus = (ROOT / "src/prometheus.rs").read_text(encoding="utf-8")
|
||||
auth = (ROOT / "src/auth/mod.rs").read_text(encoding="utf-8")
|
||||
auth_store = (ROOT / "src/auth/store.rs").read_text(encoding="utf-8")
|
||||
upstream = (ROOT / "src/upstream.rs").read_text(encoding="utf-8")
|
||||
vhosts_js = (ROOT / "src/admin/web/vhosts.js").read_text(encoding="utf-8")
|
||||
docker_source = (ROOT / "src/docker.rs").read_text(encoding="utf-8")
|
||||
http3 = rust_source(ROOT, "src/http3.rs")
|
||||
redirect = rust_source(ROOT, "src/redirect.rs")
|
||||
not_found = rust_source(ROOT, "src/not_found.rs")
|
||||
prometheus = rust_source(ROOT, "src/prometheus.rs")
|
||||
auth = rust_source(ROOT, "src/auth/mod.rs")
|
||||
auth_store = rust_source(ROOT, "src/auth/store.rs")
|
||||
upstream = rust_source(ROOT, "src/upstream.rs")
|
||||
vhosts_js = read_vhosts_js(ROOT)
|
||||
docker_source = rust_source(ROOT, "src/docker.rs")
|
||||
docker_js = (ROOT / "src/admin/web/docker.js").read_text(encoding="utf-8")
|
||||
docker_agent = (ROOT / "docker-agent/main.go").read_text(encoding="utf-8")
|
||||
docker_agent = read_docker_agent_source()
|
||||
html = (ROOT / "src/admin/web/index.html").read_text(encoding="utf-8")
|
||||
cache_panel_ui = (ROOT / "src/admin/web/cache-panel.js").read_text(encoding="utf-8")
|
||||
transform_panel = (ROOT / "src/admin/web/transform-panel.js").read_text(encoding="utf-8")
|
||||
app_js = (ROOT / "src/admin/web/app.js").read_text(encoding="utf-8")
|
||||
app_js = read_app_js(ROOT)
|
||||
|
||||
# 0.24.1: shared library statistics, authenticated runtime metadata and Logging in Settings.
|
||||
manifest = tomllib.loads((ROOT / "Cargo.toml").read_text(encoding="utf-8"))
|
||||
system_stats = (ROOT / "src/system_stats/mod.rs").read_text(encoding="utf-8")
|
||||
linux_stats = (ROOT / "src/system_stats/linux.rs").read_text(encoding="utf-8")
|
||||
system_stats = rust_source(ROOT, "src/system_stats/mod.rs")
|
||||
linux_stats = rust_source(ROOT, "src/system_stats/linux.rs")
|
||||
settings_ui = (ROOT / "src/admin/web/settings.js").read_text(encoding="utf-8")
|
||||
openapi_0241 = (ROOT / "src/admin/openapi.json").read_text(encoding="utf-8")
|
||||
assert not (ROOT / "src/system_stats/bsd.rs").exists()
|
||||
@@ -521,7 +544,7 @@ def source_contract_smoke() -> None:
|
||||
assert marker in admin, marker
|
||||
anonymous_block = admin.split("fn anonymous_allowed", 1)[1].split("const CERTIFICATE_API_KEY_HEADER", 1)[0]
|
||||
assert '"/api/status"' not in anonymous_block
|
||||
assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.25.0"' in openapi_0241
|
||||
assert '"RuntimeInformation"' in openapi_0241 and '"version": "0.25.1"' in openapi_0241
|
||||
assert 'id="panel-logging"' not in html
|
||||
for marker in ['id="settings-tab-logging"', 'id="settings-page-logging"', 'id="logging-form"', 'id="error-log-form"']:
|
||||
assert marker in html, marker
|
||||
@@ -567,7 +590,7 @@ def source_contract_smoke() -> None:
|
||||
assert marker.lower() in audit_0232.lower(), marker
|
||||
assert 'external certificate ownership model' in audit_0223.lower() and 'release gate' in audit_0223.lower()
|
||||
|
||||
update_check = (ROOT / "src/update_check.rs").read_text(encoding="utf-8")
|
||||
update_check = rust_source(ROOT, "src/update_check.rs")
|
||||
update_defaults = tomllib.loads((ROOT / "UPDATE_CHECK.toml").read_text(encoding="utf-8"))
|
||||
assert update_defaults["repository_url"] == "https://git.linuxiarz.pl/gru/RustProxy"
|
||||
assert update_defaults["version_url"].endswith("/raw/branch/master/Cargo.toml")
|
||||
@@ -659,11 +682,11 @@ def source_contract_smoke() -> None:
|
||||
html = (ROOT / 'src/admin/web/index.html').read_text(encoding='utf-8')
|
||||
cache_panel_js = (ROOT / 'src/admin/web/cache-panel.js').read_text(encoding='utf-8')
|
||||
management_js = (ROOT / 'src/admin/web/management.js').read_text(encoding='utf-8')
|
||||
app_js = (ROOT / 'src/admin/web/app.js').read_text(encoding='utf-8')
|
||||
vhosts_js = (ROOT / 'src/admin/web/vhosts.js').read_text(encoding='utf-8')
|
||||
app_js = read_app_js(ROOT)
|
||||
vhosts_js = read_vhosts_js(ROOT)
|
||||
cache_rs = read_cache_source()
|
||||
admin_rs = (ROOT / 'src/admin/mod.rs').read_text(encoding='utf-8')
|
||||
logging_rs = (ROOT / 'src/logging.rs').read_text(encoding='utf-8')
|
||||
admin_rs = rust_source(ROOT, 'src/admin/mod.rs')
|
||||
logging_rs = rust_source(ROOT, 'src/logging.rs')
|
||||
assert 'bindSettingsForm("pages-form", next => { next.pages = global.read(); }, "pages")' in management_js
|
||||
assert '"pages" => {' in admin_rs and 'default_pages_section_accepts_the_standard_value_payload' in admin_rs
|
||||
assert 'pub const LIVE_WINDOW: usize = 256;' in logging_rs and 'pub const HISTORY_CAPACITY: usize = 4096;' in logging_rs
|
||||
@@ -673,10 +696,10 @@ def source_contract_smoke() -> None:
|
||||
assert 'dialog.addEventListener("close", () => { if (pinning) return; expanded = null; paused = null;' in management_js
|
||||
assert 'hash_scheme.as_bytes()' in cache_rs and 'cache_key_separates_frontend_scheme' in cache_rs
|
||||
assert not (ROOT / 'src/cache.rs').exists() and (ROOT / 'src/cache/mod.rs').is_file()
|
||||
cache_mod = (ROOT / 'src/cache/mod.rs').read_text(encoding='utf-8')
|
||||
cache_mod = rust_source(ROOT, 'src/cache/mod.rs')
|
||||
assert 'impl Cache {\n include!(' not in cache_mod
|
||||
for fragment in ['lifecycle.rs', 'cold.rs', 'dashboard.rs', 'tickets.rs', 'lookup.rs']:
|
||||
assert (ROOT / 'src/cache' / fragment).read_text(encoding='utf-8').lstrip().startswith('impl Cache {')
|
||||
assert rust_source(ROOT, f'src/cache/{fragment}').lstrip().startswith('impl Cache {')
|
||||
assert 'payload["transform_metrics"] = json!(state.transform_metrics.total())' in admin_rs
|
||||
for marker in ['payload["transform_resources"]', 'payload["memory_accounting"]', 'non_cache_rss_includes_transform', 'cold_limit_bytes', 'cold_limit_entries']:
|
||||
assert marker in admin_rs or marker in cache, marker
|
||||
@@ -695,12 +718,12 @@ def source_contract_smoke() -> None:
|
||||
assert marker in app_js, marker
|
||||
assert 'envelope?.config.cache?.enabled === true && cacheToggle.value !== "off"' in vhosts_js
|
||||
assert 'groups = d.domains || [], shownGroups = visibleDomains(data, groups)' in cache_panel_js and 'replaceChildren(...shownGroups.map(row =>' in cache_panel_js
|
||||
balance_rs = (ROOT / 'src/balance.rs').read_text(encoding='utf-8')
|
||||
balance_rs = rust_source(ROOT, 'src/balance.rs')
|
||||
assert 'traffic_rate_drops_to_zero_after_an_idle_sample_window' in balance_rs and 'if delta == 0 {' in balance_rs and 'sample.rate = 0.0;' in balance_rs
|
||||
all_admin_js = "\n".join(path.read_text(encoding='utf-8') for path in (ROOT / 'src/admin/web').glob('*.js'))
|
||||
assert not re.search(r'(?<![A-Za-z])(?:window\.)?(?:alert|confirm)\s*\(', all_admin_js)
|
||||
assert 'function appConfirm(message, options = {})' in app_js and 'function apiErrorMessage(path, response, value)' in app_js
|
||||
deploy_py = (ROOT / 'scripts/deploy.py').read_text(encoding='utf-8')
|
||||
deploy_py = read_deploy_source()
|
||||
assert "SHELL_PATH_PROFILE = Path('/etc/profile.d/rustproxy-path.sh')" in deploy_py and 'install_shell_path(prefix)' in deploy_py and 'check_shell_path(prefix)' in deploy_py
|
||||
assert 'grid-column: span 3' in ui_css and '.check>span {' in ui_css and 'transform: translateY(-1px)' in ui_css
|
||||
assert '.cache-toolbar-status .check' in ui_css and '.docker-toolbar .check' in ui_css and '.docker-alias-field>span:first-child {' in ui_css and 'margin-bottom: 0' in ui_css and '.docker-alias-editor {' in ui_css and 'min-height: 38px' in ui_css and 'height: 30px' in ui_css
|
||||
@@ -710,7 +733,7 @@ def source_contract_smoke() -> None:
|
||||
screenshots = {path.name for path in (ROOT / 'docs/screenshots').iterdir() if path.is_file()}
|
||||
assert 'README.md' in screenshots
|
||||
assert screenshots <= {'README.md', 'login.png', 'location-headers.png'}
|
||||
assert 'generated_vhost_inherits_global_server_header_policy' in (ROOT / 'src/docker.rs').read_text(encoding='utf-8')
|
||||
assert 'generated_vhost_inherits_global_server_header_policy' in rust_source(ROOT, 'src/docker.rs')
|
||||
assert '.docker-route-grid {' in ui_css and 'align-items: start' in ui_css
|
||||
assert '.docker-acl-grid {' in ui_css and 'height: 38px' in ui_css
|
||||
assert 'preserve.checked = assignment?.preserve_host !== false' in docker_js
|
||||
@@ -724,7 +747,7 @@ def source_contract_smoke() -> None:
|
||||
'pub targets_admin_listener: bool',
|
||||
]:
|
||||
assert marker in config, marker
|
||||
vhost_source = (ROOT / "src/vhost.rs").read_text(encoding="utf-8")
|
||||
vhost_source = rust_source(ROOT, "src/vhost.rs")
|
||||
assert 'is_legacy_main_fallback' in vhost_source
|
||||
assert "benign_tunnel_close" in websocket and "ConnectionReset" in websocket
|
||||
assert 'StatusCode::UNAUTHORIZED => Some(include_str!("admin/web/error_401.html"))' in pages
|
||||
@@ -744,7 +767,7 @@ def source_contract_smoke() -> None:
|
||||
assert marker in proxy, marker
|
||||
for marker in ["force_ssl_redirect", "force_ssl_location", "StatusCode::PERMANENT_REDIRECT", "https_redirect_port", "apply_upstream"]:
|
||||
assert marker in proxy, marker
|
||||
forwarding = (ROOT / "src/forwarding.rs").read_text(encoding="utf-8")
|
||||
forwarding = rust_source(ROOT, "src/forwarding.rs")
|
||||
for marker in ["pub fn apply_upstream", 'headers.remove("x-forwarded-for")', 'headers.remove("x-real-ip")', "upstream_can_withhold_client_ip_without_losing_origin_metadata"]:
|
||||
assert marker in forwarding, marker
|
||||
for marker in ["SINK_QUEUE", "FileSink", "LogstashSink", "LokiSink", "Limited::new"]:
|
||||
@@ -784,7 +807,7 @@ def source_contract_smoke() -> None:
|
||||
"matching_cold_key",
|
||||
]:
|
||||
assert marker in cache, marker
|
||||
transform = (ROOT / "src/transform/mod.rs").read_text(encoding="utf-8")
|
||||
transform = rust_source(ROOT, "src/transform/mod.rs")
|
||||
for marker in [
|
||||
"pub struct Settings", "pub struct Overrides",
|
||||
"resolved.enabled = global.enabled && self.enabled",
|
||||
@@ -805,20 +828,20 @@ def source_contract_smoke() -> None:
|
||||
assert marker in transform, marker
|
||||
assert 'collect bounded static response for transformation: {error}' in transform
|
||||
assert '.context("collect bounded static response for transformation")' not in transform
|
||||
lookup = (ROOT / 'src/cache/lookup.rs').read_text(encoding='utf-8')
|
||||
model = (ROOT / 'src/cache/model.rs').read_text(encoding='utf-8')
|
||||
css_minifier = (ROOT / 'src/transform/css.rs').read_text(encoding='utf-8')
|
||||
lookup = rust_source(ROOT, 'src/cache/lookup.rs')
|
||||
model = rust_source(ROOT, 'src/cache/model.rs')
|
||||
css_minifier = rust_source(ROOT, 'src/transform/css.rs')
|
||||
assert 'let Some(_read_lease) = self.try_reserve_transient' in lookup
|
||||
assert 'struct TransientLease {\n _inner: Arc<TransientLeaseInner>,' in model
|
||||
assert 'struct TransientLease(Arc<TransientLeaseInner>);' not in model
|
||||
assert 'last_char = out.chars().last();' not in css_minifier
|
||||
assert css_minifier.count("last_char = Some(' ');") == 1
|
||||
assert "pub mod transform;" in (ROOT / "src/lib.rs").read_text(encoding="utf-8")
|
||||
assert "pub mod transform;" in rust_source(ROOT, "src/lib.rs")
|
||||
for name in ["js.rs", "css.rs"]:
|
||||
minifier = (ROOT / "src/transform" / name).read_text(encoding="utf-8")
|
||||
assert "Vec<char>" not in minifier and ".chars().collect" not in minifier
|
||||
assert "into_bytes()" in minifier
|
||||
balance = (ROOT / "src/balance.rs").read_text(encoding="utf-8")
|
||||
balance = rust_source(ROOT, "src/balance.rs")
|
||||
for marker in [
|
||||
"pub struct PassiveHealth", "enabled: false", "pub passive_health: PassiveHealth",
|
||||
"passive_health_is_opt_in_and_uses_separate_thresholds",
|
||||
@@ -839,12 +862,12 @@ def source_contract_smoke() -> None:
|
||||
]:
|
||||
assert marker in cache, marker
|
||||
assert 'impl Drop for Candidate' not in cache
|
||||
diagnostics = (ROOT / "src/diagnostics.rs").read_text(encoding="utf-8") + (ROOT / "src/system_stats/mod.rs").read_text(encoding="utf-8") + (ROOT / "src/system_stats/linux.rs").read_text(encoding="utf-8")
|
||||
diagnostics = rust_source(ROOT, "src/diagnostics.rs") + rust_source(ROOT, "src/system_stats/mod.rs") + rust_source(ROOT, "src/system_stats/linux.rs")
|
||||
for marker in ['process_rss_anon_bytes', 'process_rss_file_bytes', 'process_rss_shmem_bytes', 'process_data_bytes', 'process_swap_bytes', 'RssAnon', 'RssFile', 'RssShmem', 'VmData', 'VmSwap']:
|
||||
assert marker in diagnostics, marker
|
||||
for marker in ['admin_session_passthrough', 'admin_control_plane', 'restore_admin_cookie', 'BEFORE returning a successful RFC 9220 response', 'websocket_phase("tunnel_ready")']:
|
||||
assert marker in proxy, marker
|
||||
headers_source = (ROOT / "src/proxy/headers.rs").read_text(encoding="utf-8")
|
||||
headers_source = rust_source(ROOT, "src/proxy/headers.rs")
|
||||
assert 'pub fn restore_admin_cookie' in headers_source and 'admin_cookie_can_be_restored_only_from_captured_fragment' in headers_source
|
||||
assert "state.cache.purge(None)" not in proxy
|
||||
assert "request.method() == Method::HEAD && cache_ticket.is_some()" in proxy
|
||||
@@ -860,14 +883,14 @@ def source_contract_smoke() -> None:
|
||||
assert marker in not_found, marker
|
||||
assert "RedirectResponse" in proxy and "snapshot.redirect_on" in proxy
|
||||
assert "#[serde(default)]\n pub tls_verify: bool" in config
|
||||
upstream = (ROOT / "src/upstream.rs").read_text(encoding="utf-8")
|
||||
upstream = rust_source(ROOT, "src/upstream.rs")
|
||||
assert "BackendCertificateVerificationDisabled { supported_schemes }" in upstream
|
||||
disabled_verifier = upstream.split("impl ServerCertVerifier for BackendCertificateVerificationDisabled", 1)[1].split("pub fn client(", 1)[0]
|
||||
assert disabled_verifier.count("Ok(HandshakeSignatureValid::assertion())") == 2
|
||||
assert "WebPkiServerVerifier" not in disabled_verifier
|
||||
assert "stream_metrics" in runtime and "stream_metrics" in main
|
||||
for marker in ["PrefixPriority", "RegexInsensitive", "LocationResponse", "pub rewrite: PathRewrite"]:
|
||||
assert marker in (ROOT / "src/vhost.rs").read_text(encoding="utf-8"), marker
|
||||
assert marker in rust_source(ROOT, "src/vhost.rs"), marker
|
||||
for marker in ["rewritten_path", "nginx_style_location_precedence_exact_priority_regex_prefix", "location_rewrites_are_normalized_and_regex_captures_expand", "custom_location_response_is_validated"]:
|
||||
assert marker in config, marker
|
||||
for marker in ["local_location_response", "route.rewritten_path(&path)", "encoded_origin_path", "client_ip.to_string()"]:
|
||||
@@ -885,17 +908,17 @@ def source_contract_smoke() -> None:
|
||||
assert '<div class="edition">INDEPENDENT BY DESIGN <span>' not in html
|
||||
assert '<div class="edition">INDEPENDENT BY DESIGN</div>' in html
|
||||
assert "inspected_metadata" in admin and "certificate_metadata" in admin
|
||||
balance = (ROOT / "src/balance.rs").read_text(encoding="utf-8")
|
||||
balance = rust_source(ROOT, "src/balance.rs")
|
||||
assert "max_concurrent_requests" in balance and "queue_timeout_ms" in balance and "select_retry_wait" in balance
|
||||
for marker in ["dns_errors_total", "tls_errors_total", "response_header_timeouts_total", "last_failure_detail", "last_recovered_ms"]:
|
||||
assert marker in balance, marker
|
||||
tls_source = (ROOT / "src/tls.rs").read_text(encoding="utf-8")
|
||||
tls_source = rust_source(ROOT, "src/tls.rs")
|
||||
for marker in ["CertificateMetadata", "not_before_unix", "not_after_unix", "days_remaining", "expiry_status"]:
|
||||
assert marker in tls_source, marker
|
||||
assert '"/assets/redirects.js"' in admin and 'include_str!("web/redirects.js")' in admin and '.route("/redirects", get(public::index))' in admin
|
||||
assert '/Ubuntu%2014.04%20LTS/' in acl and 'repeated_slashes_are_normalized_for_routing' in acl and 'percent_encoded_spaces_are_valid_request_paths' in policies and 'repeated_slashes_route_normally_but_reach_upstream_unchanged' in policies and 'local_location_response_uses_response_rules_without_opening_backend' in policies and 'local_location_response_still_respects_route_acl' in policies
|
||||
db_source = (ROOT / "src/db.rs").read_text(encoding="utf-8")
|
||||
acme_source = (ROOT / "src/acme.rs").read_text(encoding="utf-8")
|
||||
db_source = rust_source(ROOT, "src/db.rs")
|
||||
acme_source = rust_source(ROOT, "src/acme.rs")
|
||||
assert "SCHEMA_VERSION: i64 = 7" in db_source and "certificate_renewals" in db_source and "0004_admin_preferences.sql" in db_source and "0005_certificate_api.sql" in db_source and "0006_certificate_api_credentials.sql" in db_source and "0007_secret_store.sql" in db_source
|
||||
assert "load_renewal_observations" not in acme_source and "renewals.json" not in acme_source
|
||||
for marker in ["pub kind: &'static str", "pub log_directory: String", "pub(crate) fn certbot_log_directory", 'kind: "issue"', '"renewal-dry-run"']:
|
||||
@@ -903,28 +926,28 @@ def source_contract_smoke() -> None:
|
||||
assert '"log_directories":log_directories' in admin and 'crate::acme::certbot_log_directory' in admin
|
||||
assert "store.load_certificate_renewals().await?" in main and "state.acme.restore_renewals(renewal_observations)" in main
|
||||
assert 'accept-remote-admin-risk' in main and 'hide = true' in main
|
||||
settings_rust = (ROOT / 'src/settings.rs').read_text(encoding='utf-8')
|
||||
settings_rust = rust_source(ROOT, 'src/settings.rs')
|
||||
assert 'pub struct StartupLocks' in settings_rust and 'fixed by systemd/startup' in settings_rust
|
||||
logging_rust = (ROOT / 'src/logging.rs').read_text(encoding='utf-8')
|
||||
logging_rust = rust_source(ROOT, 'src/logging.rs')
|
||||
assert 'pub per_vhost: bool' in logging_rust and 'default_vhost_log_directory' in logging_rust
|
||||
pages_rust = (ROOT / 'src/pages.rs').read_text(encoding='utf-8')
|
||||
pages_rust = rust_source(ROOT, 'src/pages.rs')
|
||||
assert 'validate_local_response' in pages_rust and '(200..=599).contains(&status)' in pages_rust
|
||||
assert 'Kind::Builtin' in pages_rust and 'builtin_application_page' in pages_rust
|
||||
docker_rust = (ROOT / 'src/docker.rs').read_text(encoding='utf-8')
|
||||
docker_rust = rust_source(ROOT, 'src/docker.rs')
|
||||
for marker in ['pub poll_interval_seconds: u64', 'pub acl_profile_id: Option<String>', 'pub proxy_timeouts: crate::vhost::ProxyTimeouts', 'refresh(&self, settings: &Settings, force: bool)']:
|
||||
assert marker in docker_rust, marker
|
||||
docker_ui = (ROOT / 'src/admin/web/docker.js').read_text(encoding='utf-8')
|
||||
for marker in ['docker-live-refresh', 'saveAssignment(agentId, row, publish)', 'docker-acl-profile', 'docker-proxy-read', 'dockerOpenApp', 'proxiedUrl(agent, hostname)']:
|
||||
assert marker in docker_ui, marker
|
||||
compression_source = (ROOT / 'src/compression.rs').read_text(encoding='utf-8')
|
||||
metrics_source = (ROOT / 'src/metrics.rs').read_text(encoding='utf-8')
|
||||
compression_source = rust_source(ROOT, 'src/compression.rs')
|
||||
metrics_source = rust_source(ROOT, 'src/metrics.rs')
|
||||
style_source = (ROOT / 'src/admin/web/style.css').read_text(encoding='utf-8')
|
||||
app_source = (ROOT / 'src/admin/web/app.js').read_text(encoding='utf-8')
|
||||
vhosts_ui = (ROOT / 'src/admin/web/vhosts.js').read_text(encoding='utf-8')
|
||||
app_source = read_app_js(ROOT)
|
||||
vhosts_ui = read_vhosts_js(ROOT)
|
||||
for marker in ['pub enum AppliedEncoding', 'AppliedEncoding::Gzip', 'AppliedEncoding::Brotli', 'AppliedEncoding::Zstd', 'AppliedEncoding::Deflate']:
|
||||
assert marker in compression_source, marker
|
||||
assert 'quality_weights_can_select_each_enabled_codec' in compression_source
|
||||
diagnostics_source = (ROOT / 'src/diagnostics.rs').read_text(encoding='utf-8') + (ROOT / 'src/system_stats/mod.rs').read_text(encoding='utf-8') + (ROOT / 'src/system_stats/linux.rs').read_text(encoding='utf-8')
|
||||
diagnostics_source = rust_source(ROOT, 'src/diagnostics.rs') + rust_source(ROOT, 'src/system_stats/mod.rs') + rust_source(ROOT, 'src/system_stats/linux.rs')
|
||||
for marker in ['pub struct NetworkInterfaceView', 'pub network_interfaces: Vec<NetworkInterfaceView>', 'sysinfo::Networks::new_with_refreshed_list', 'system.network_stats', 'collect_network_systemstat', 'normalize_network_interfaces', 'merge_network_interfaces']:
|
||||
assert marker in diagnostics_source, marker
|
||||
assert 'interfaces.truncate(64)' not in diagnostics_source, 'network inventory must not silently truncate interfaces'
|
||||
@@ -942,14 +965,14 @@ def source_contract_smoke() -> None:
|
||||
assert 'sparkline-h1' in (ROOT / 'src/admin/web/index.html').read_text(encoding='utf-8')
|
||||
assert 'body[data-density="compact"]' in style_source and '--ui-control: 22px' in style_source
|
||||
assert 'status.backend_pool_domains' in vhosts_ui and 'payload["backend_pool_domains"]' in admin
|
||||
docker_source = (ROOT / 'src/docker.rs').read_text(encoding='utf-8')
|
||||
docker_source = rust_source(ROOT, 'src/docker.rs')
|
||||
management_ui = (ROOT / 'src/admin/web/management.js').read_text(encoding='utf-8')
|
||||
for marker in ['function aliasEditor(', 'host-chip-editor docker-alias-editor', '.docker-force-ssl', '.docker-websockets', '.docker-cache-enabled', '.docker-passive-enabled', '.docker-passive-5xx', '.docker-agent-acl-profile']:
|
||||
assert marker in docker_ui, marker
|
||||
for marker in ['"force_ssl": assignment.force_ssl.unwrap_or(false)', '"websockets": assignment.websockets', '"cache": {"enabled": assignment.cache_enabled}', '"passive_health": assignment.passive_health.clone()', 'assignment.acl_profile_id.clone().or_else(|| agent.acl_profile_id.clone())']:
|
||||
assert marker in docker_source, marker
|
||||
assert 'agent.acl_profile_id === profile.id' in management_ui
|
||||
vhost_source = (ROOT / 'src/vhost.rs').read_text(encoding='utf-8')
|
||||
vhost_source = rust_source(ROOT, 'src/vhost.rs')
|
||||
for marker in ['DEFAULT_PROXY_CONNECT_TIMEOUT_SECONDS: u64 = 10', 'DEFAULT_PROXY_RESPONSE_HEADER_TIMEOUT_SECONDS: u64 = 120', 'DEFAULT_PROXY_READ_TIMEOUT_SECONDS: u64 = 60', 'DEFAULT_PROXY_SEND_TIMEOUT_SECONDS: u64 = 60', 'proxy_timeouts_have_explicit_safe_defaults_but_keep_null_compatibility']:
|
||||
assert marker in vhost_source, marker
|
||||
assert 'defaultProxyTimeouts = () => ({ connect_timeout_seconds: 10, response_header_timeout_seconds: 120, read_timeout_seconds: 60, send_timeout_seconds: 60 })' in vhosts_ui
|
||||
@@ -958,22 +981,22 @@ def source_contract_smoke() -> None:
|
||||
assert 'docker-agent-status-badge' in style_source and 'justify-content: center' in style_source
|
||||
assert '.docker-agent-grid>label' in style_source and '#docker-agent-add' in style_source and 'margin-top: 14px' in style_source
|
||||
assert 'Bearer token:' in (ROOT / 'docker-agent/install.sh').read_text(encoding='utf-8')
|
||||
tls_rust = (ROOT / 'src/tls.rs').read_text(encoding='utf-8')
|
||||
tls_rust = rust_source(ROOT, 'src/tls.rs')
|
||||
assert 'pub fn validate_certificate_removals' in tls_rust and 'pub fn effective_cipher_suites' in tls_rust
|
||||
assert "renewals.json" not in main
|
||||
upgrade_source = (ROOT / "scripts/upgrade.py").read_text(encoding="utf-8")
|
||||
assert "removed_obsolete_runtime_state" in upgrade_source and "legacy-data" not in upgrade_source and "certs_dir.is_symlink()" in upgrade_source
|
||||
assert '.managed-update' in main and "flush_hot_to_cold_for_update" in main
|
||||
openapi = (ROOT / "src/admin/openapi.json").read_text(encoding="utf-8")
|
||||
for marker in ['"version": "0.25.0"', '"memory_preset"', '"cold_preset"', '"memory_budget_bytes"', '"max_inflight"', '"preset"', '"session_days"', '"session_lifetime_seconds"', '"accept_local_listener_risk"', '"backend_header"', '"backend_timing_header"', '"cold_on_age"', '"match_type"', '"regex_insensitive"', '"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']:
|
||||
for marker in ['"version": "0.25.1"', '"memory_preset"', '"cold_preset"', '"memory_budget_bytes"', '"max_inflight"', '"preset"', '"session_days"', '"session_lifetime_seconds"', '"accept_local_listener_risk"', '"backend_header"', '"backend_timing_header"', '"cold_on_age"', '"match_type"', '"regex_insensitive"', '"NotFoundHost"', '"not_found_hosts"', '"PrometheusSettings"', '"allow_trusted_network_without_token"', '"include_docker_vhosts"', '"include_streams"', '"include_logging"']:
|
||||
assert marker in openapi, marker
|
||||
assert '"software_update"' in openapi and '"update_available"' in openapi
|
||||
for marker in ['"/api/certificates/deploy"', '"CertificateDeploy"', '"CertificateDeployResult"', '"/api/certificates/reload"']:
|
||||
assert marker in openapi, marker
|
||||
for marker in ['"/api/certificate-api"', '"/api/certificate-api/{key_id}"', '"/api/certificates/{certificate_id}/reload"', '"CertificateReloadOneResult"', '"CertificateApiBearer"', '"CertificateApiKey"', '"/api/live-logs"']:
|
||||
assert marker in openapi, marker
|
||||
websocket_source = (ROOT / 'src/proxy/websocket.rs').read_text(encoding='utf-8')
|
||||
proxy_source = (ROOT / 'src/proxy/mod.rs').read_text(encoding='utf-8')
|
||||
websocket_source = rust_source(ROOT, 'src/proxy/websocket.rs')
|
||||
proxy_source = rust_source(ROOT, 'src/proxy/mod.rs')
|
||||
for marker in ['websocket_legacy_support', 'vhWebsocketLegacySupport', 'vhWebsocketLegacySupportHelp']:
|
||||
assert marker in vhosts_ui or marker in openapi, marker
|
||||
for marker in ['fn legacy_websocket_origin(', 'request.headers_mut().insert("origin", origin)', 'websocket_legacy_support && !websocket_upstream_h2']:
|
||||
@@ -982,7 +1005,7 @@ def source_contract_smoke() -> None:
|
||||
assert marker in websocket_source, marker
|
||||
for marker in ['certificate_api_credentials', 'upsert_certificate_api_credential', 'revoke_certificate_api_credential', 'verify_certificate_api_credential']:
|
||||
assert marker in db_source, marker
|
||||
logging_source = (ROOT / 'src/logging.rs').read_text(encoding='utf-8')
|
||||
logging_source = rust_source(ROOT, 'src/logging.rs')
|
||||
for marker in ['LIVE_STREAM_CAPACITY', 'broadcast::Sender<LogEntry>', 'pub fn subscribe']:
|
||||
assert marker in logging_source, marker
|
||||
for marker in ['livelogs: bool', 'LiveLogFilters', 'live_logs_command', 'LocalAdminCertificateVerifier', 'detect_local_admin_scheme', 'admin.uri("/api/live-logs")']:
|
||||
@@ -1005,14 +1028,14 @@ def source_contract_smoke() -> None:
|
||||
assert '127.0.0.1:9090' in transport_doc and 'accept_local_listener_risk: true' in transport_doc
|
||||
drop_impl = http3.split('impl Drop for ReceiveBody', 1)[1].split('#[cfg(test)]', 1)[0]
|
||||
assert 'self.stream.stop_sending(' not in drop_impl
|
||||
websocket_source = (ROOT / 'src/proxy/websocket.rs').read_text(encoding='utf-8')
|
||||
websocket_source = rust_source(ROOT, 'src/proxy/websocket.rs')
|
||||
for marker in ['h3::quinn::RecvStream', 'websocket_rfc9220_on(local)', 'run_h3', 'H3_REQUEST_CANCELLED']:
|
||||
assert marker in http3 or marker in websocket_source, marker
|
||||
assert 'upstream: Upgraded' in websocket_source
|
||||
assert 'timeout(Duration::from_secs(10), upstream)' not in websocket_source
|
||||
for marker in ['websocket_phase("tunnel_started")', 'websocket_phase("tunnel_closed")', 'h3_reset("H3_REQUEST_CANCELLED")']:
|
||||
assert marker in websocket_source, marker
|
||||
logging_source = (ROOT / 'src/logging.rs').read_text(encoding='utf-8')
|
||||
logging_source = rust_source(ROOT, 'src/logging.rs')
|
||||
for marker in ['http_version', 'transport', 'websocket_bootstrap', 'websocket_phase', 'websocket_error', 'h3_reset_code', 'upstream_status']:
|
||||
assert marker in logging_source, marker
|
||||
assert 'h3_quinn' not in http3 and 'h3_quinn' not in (ROOT / 'tests/http3.rs').read_text(encoding='utf-8')
|
||||
@@ -1082,13 +1105,32 @@ def reproducible_archive_smoke(tmp: Path) -> None:
|
||||
with zipfile.ZipFile(first) as archive:
|
||||
names = archive.namelist()
|
||||
assert "RustProxy/SHA256SUMS" in names
|
||||
assert "RustProxy/scripts/rust_source.py" in names
|
||||
assert "RustProxy/scripts/rust_module_contracts.py" in names
|
||||
assert "RustProxy/scripts/source_module_contracts.py" in names
|
||||
for name in names:
|
||||
if name.startswith("RustProxy/") and name.endswith(".rs"):
|
||||
assert len(archive.read(name).decode("utf-8").splitlines()) <= 500, name
|
||||
if name.startswith("RustProxy/src/admin/web/") and name.endswith(".js"):
|
||||
assert len(archive.read(name).decode("utf-8").splitlines()) <= 500, name
|
||||
if name.startswith("RustProxy/docker-agent/") and name.endswith(".go"):
|
||||
assert len(archive.read(name).decode("utf-8").splitlines()) <= 500, name
|
||||
assert "RustProxy/src/admin/web/error_401.html" in names
|
||||
assert "RustProxy/src/admin/web/error_404.html" in names
|
||||
assert "RustProxy/docs/CUSTOM_LOCATIONS.md" in names
|
||||
assert "RustProxy/docs/WEB_SERVER.md" in names
|
||||
assert "RustProxy/docs/PHP_FPM.md" in names
|
||||
assert "RustProxy/src/web_server.rs" in names
|
||||
for module in ["mod.rs", "config.rs", "filesystem.rs", "security.rs", "response.rs", "request.rs", "routing.rs", "fastcgi.rs", "serve.rs", "htaccess.rs", "tests.rs"]:
|
||||
assert f"RustProxy/src/web_server/{module}" in names
|
||||
assert "RustProxy/src/web_server.rs" not in names
|
||||
assert "RustProxy/src/admin/web/web-server.js" in names
|
||||
for module in ["app/core.js", "app/runtime.js", "app/settings.js", "vhosts/core.js", "vhosts/editor.js", "vhosts/render.js"]:
|
||||
assert f"RustProxy/src/admin/web/{module}" in names
|
||||
assert "RustProxy/src/admin/web/app.js" not in names and "RustProxy/src/admin/web/vhosts.js" not in names
|
||||
for module in ["model.go", "docker.go", "network.go", "certificate.go", "server.go", "main.go", "main_test.go"]:
|
||||
assert f"RustProxy/docker-agent/{module}" in names
|
||||
for module in ["deploy.py", "deploy_core.py", "deploy_runtime.py", "migrate_npm.py", "migrate_npm_core.py", "migrate_npm_api.py"]:
|
||||
assert f"RustProxy/scripts/{module}" in names
|
||||
assert "RustProxy/nagios/check_rustproxy.py" in names
|
||||
assert "RustProxy/nagios/check_rustproxy_api" in names
|
||||
assert "RustProxy/src/admin/web/host404.js" in names
|
||||
@@ -1108,6 +1150,8 @@ def reproducible_archive_smoke(tmp: Path) -> None:
|
||||
|
||||
|
||||
def main() -> None:
|
||||
subprocess.run(["python3", str(ROOT / "scripts/rust_module_contracts.py")], check=True)
|
||||
subprocess.run(["python3", str(ROOT / "scripts/source_module_contracts.py")], check=True)
|
||||
with tempfile.TemporaryDirectory(prefix="rustproxy-release-smoke-") as directory:
|
||||
tmp = Path(directory)
|
||||
sql_and_backup_smoke(tmp / "sql")
|
||||
@@ -1119,7 +1163,7 @@ def main() -> None:
|
||||
source_contract_smoke()
|
||||
container_contract_smoke()
|
||||
debian_grafana_contract_smoke()
|
||||
print("PASS: 0.25.0 SQLite/upgrade, local-response 404 hosts, Prometheus/Grafana diagnostics, Debian packaging, cache/dashboard/logging/Nagios, retained admin/H3/stream/TLS contracts and reproducible source archives.")
|
||||
print("PASS: 0.25.1 SQLite/upgrade, local-response 404 hosts, Prometheus/Grafana diagnostics, Debian packaging, cache/dashboard/logging/Nagios, retained admin/H3/stream/TLS contracts and reproducible source archives.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in new issue
Block a user