fadenstack 0.5.0rc4__tar.gz → 0.5.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/PKG-INFO +1 -1
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/pyproject.toml +1 -1
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/__init__.py +1 -1
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/docker-compose.yaml +6 -1
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/nginx/rate-limits.conf +12 -3
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/deploy.py +5 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/upgrade.py +4 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/bundle.py +10 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/compose.py +10 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/registry.py +63 -1
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/rmq.py +8 -1
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/secret_files.py +3 -1
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/conftest.py +20 -2
- fadenstack-0.5.1/tests/test_install_extensions.py +158 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_release_bundle.py +5 -1
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_tune_memory.py +12 -1
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/.gitignore +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/LICENSE +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/NOTICE +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/README.md +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/hatch_build.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/__main__.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/pii-catalog.json +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/.gitignore +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/LICENSE +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/NOTICE +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/README.md +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/agent-binaries/.gitignore +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/alloy/config.alloy +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/clickhouse/config.d/system-logs.xml +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/docker-compose.gpu.yaml +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/docker-compose.pii-gpu.yaml +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/grafana/dashboards/templates/vllm-runtime.json +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/grafana/provisioning/dashboards/dashboard.yml +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/grafana/provisioning/dashboards/json/faden-overview.json +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/grafana/provisioning/datasources/ds.yaml +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/grafana/provisioning/datasources/prometheus.yaml +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/initdb/01-init.sql +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/loki/local-config.yaml +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/nginx/locations.conf +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/nginx/maintenance.html +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/nginx/nginx-tls.conf +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/nginx/nginx.conf +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/nginx/security-headers.conf +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/nginx/select-config.sh +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/prometheus/prometheus.yml +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/rabbitmq/definitions.json.example +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/tls/README.md +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/cli.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/__init__.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/admin.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/audit.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/backup.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/chats.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/config.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/dev/__init__.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/dev/dev_doctor.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/dev/dev_down.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/dev/dev_group.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/dev/dev_init.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/dev/dev_pull.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/dev/dev_status.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/dev/dev_up.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/doctor.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/down.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/logs_cmd.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/maintenance.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/module.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/observe.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/ports.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/rebuild.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/restore.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/runtime_images.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/secrets.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/status.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/tls.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/tune.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/up.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/commands/version.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/__init__.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/api_client.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/backend_tool.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/backup.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/backup_crypto.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/backup_key.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/bootstrap.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/chat_retention.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/clickhouse.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/console.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/detect.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/dev_modules.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/docker_env.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/edge.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/env_gen.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/git.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/install.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/installation.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/local_node.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/maintenance.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/pii_artifacts.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/pii_catalog.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/pii_hardware.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/pii_runtime.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/private_files.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/provenance_keys.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/release_feed.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/release_notice.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/runtime_images.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/schedule.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/sealing.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/secret_rotation.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/settings.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/sysinfo.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/tls.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/tune.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/update_check.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/core/workspace.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/templates/env.j2 +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/tui/__init__.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/tui/widgets/__init__.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/tui/wizard/__init__.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_backup_encryption.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_backup_migration_heads.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_backup_retention.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_backup_schedule.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_backup_tls.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_backup_volumes.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_binary_notices.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_bootstrap_token.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_chat_retention.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_cli_output_encoding.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_data_dir_move.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_data_volumes_logs.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_deploy_summary.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_dev_env_loopback.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_dev_modules.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_dev_up_port_holders.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_dev_up_reclaim.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_dev_up_service_hosts.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_dev_up_terminal.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_docker_env.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_doctor_ports.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_edge.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_generated_env_reaches_compose.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_inactive_modules.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_installation.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_internal_token_env.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_local_node.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_main.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_maintenance.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_module_env.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_module_secrets.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_pii_artifacts.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_pii_catalog.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_pii_hardware.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_pii_runtime.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_pii_upgrade.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_postgres_logins.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_private_files.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_provenance_keys.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_provenance_module.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_provenance_on_by_default.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_rebuild.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_redis_limits.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_registry_gateway_env.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_release_feed.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_release_notice.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_release_versions.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_required_env.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_restore_maintenance.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_runtime_images.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_secret_files.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_secret_rotation.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_service_logins.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_shared_compose_ports.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_tls.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_tls_renew.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_trusted_keys.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_tune.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_update_check.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_update_feed.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_upgrade_housekeeping.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_upgrade_rabbitmq.py +0 -0
- {fadenstack-0.5.0rc4 → fadenstack-0.5.1}/tests/test_workspace_detection.py +0 -0
|
@@ -340,7 +340,10 @@ services:
|
|
|
340
340
|
resources:
|
|
341
341
|
limits:
|
|
342
342
|
cpus: "1"
|
|
343
|
-
|
|
343
|
+
# RabbitMQ is told this same amount (rabbitmq/10-faden.conf, written by the CLI's core/rmq.py): it reads
|
|
344
|
+
# the host's memory otherwise and slows publishers only at 60 % of that, long after the kernel has killed
|
|
345
|
+
# it here. 512M was too little at start-up after a reboot (recovering its data, every service starting).
|
|
346
|
+
memory: 1G
|
|
344
347
|
healthcheck:
|
|
345
348
|
test: rabbitmq-diagnostics check_running -q
|
|
346
349
|
interval: 3s
|
|
@@ -610,6 +613,7 @@ services:
|
|
|
610
613
|
FADEN_BACKEND_SKILLS_ENABLED: ${FADEN_BACKEND_SKILLS_ENABLED:-false}
|
|
611
614
|
FADEN_BACKEND_MAILER_SERVICE_URL: ${FADEN_BACKEND_MAILER_SERVICE_URL:-http://faden-mailer:8000}
|
|
612
615
|
FADEN_BACKEND_AUTH_SERVICE_URL: ${FADEN_BACKEND_AUTH_SERVICE_URL:-http://faden-auth:8000}
|
|
616
|
+
FADEN_BACKEND_AUTH_SERVICE_TOKEN: ${FADEN_BACKEND_AUTH_SERVICE_TOKEN:-}
|
|
613
617
|
FADEN_BACKEND_DOCLING_SERVICE_URL: ${FADEN_BACKEND_DOCLING_SERVICE_URL:-http://faden-docling:8000}
|
|
614
618
|
FADEN_BACKEND_SKILLS_SERVICE_URL: ${FADEN_BACKEND_SKILLS_SERVICE_URL:-http://faden-skills:8000}
|
|
615
619
|
FADEN_BACKEND_SKILLS_SERVICE_TOKEN: ${FADEN_BACKEND_SKILLS_SERVICE_TOKEN:?set it in .env, as faden deploy and faden upgrade do (Help, Installing, Secrets in .env)}
|
|
@@ -731,6 +735,7 @@ services:
|
|
|
731
735
|
FADEN_BACKEND_MCP_SERVICE_TOKEN: ${FADEN_BACKEND_MCP_SERVICE_TOKEN:?set it in .env, as faden deploy and faden upgrade do (Help, Installing, Secrets in .env)}
|
|
732
736
|
FADEN_BACKEND_MAILER_SERVICE_URL: ${FADEN_BACKEND_MAILER_SERVICE_URL:-http://faden-mailer:8000}
|
|
733
737
|
FADEN_BACKEND_AUTH_SERVICE_URL: ${FADEN_BACKEND_AUTH_SERVICE_URL:-http://faden-auth:8000}
|
|
738
|
+
FADEN_BACKEND_AUTH_SERVICE_TOKEN: ${FADEN_BACKEND_AUTH_SERVICE_TOKEN:-}
|
|
734
739
|
FADEN_BACKEND_DOCLING_SERVICE_URL: ${FADEN_BACKEND_DOCLING_SERVICE_URL:-http://faden-docling:8000}
|
|
735
740
|
FADEN_BACKEND_SKILLS_SERVICE_URL: ${FADEN_BACKEND_SKILLS_SERVICE_URL:-http://faden-skills:8000}
|
|
736
741
|
FADEN_BACKEND_SKILLS_SERVICE_TOKEN: ${FADEN_BACKEND_SKILLS_SERVICE_TOKEN:?set it in .env, as faden deploy and faden upgrade do (Help, Installing, Secrets in .env)}
|
|
@@ -11,9 +11,13 @@
|
|
|
11
11
|
# for everyone, shares one count. Over the limit nginx answers 429 at
|
|
12
12
|
# once (nodelay) and the next try a few seconds later goes through.
|
|
13
13
|
#
|
|
14
|
-
# faden_signin console and API sign-in
|
|
15
|
-
#
|
|
16
|
-
#
|
|
14
|
+
# faden_signin console and API sign-in, every step of a sign-in with an
|
|
15
|
+
# outside provider, and the password or code that links one
|
|
16
|
+
# to an existing account: 20 a minute, 20 at once.
|
|
17
|
+
# faden_account forgotten and reset passwords (by mail or by a link an
|
|
18
|
+
# administrator made), e-mail verification, the code mailed
|
|
19
|
+
# to link an outside sign-in, and bootstrap: 10 a minute,
|
|
20
|
+
# 10 at once.
|
|
17
21
|
# faden_refresh agents renewing their tokens: 300 a minute, 200 at once.
|
|
18
22
|
# Each running agent renews about every 10 minutes
|
|
19
23
|
# (FADEN_BACKEND_AGENT_ACCESS_TOKEN_TTL_S); the SDKs keep
|
|
@@ -26,11 +30,16 @@
|
|
|
26
30
|
map "$request_method $uri" $faden_signin_key {
|
|
27
31
|
default "";
|
|
28
32
|
"~^POST /api/auth/(jwt|cookie)/login$" $binary_remote_addr;
|
|
33
|
+
"~^GET /api/auth/sso/[a-z0-9-]+/(start|callback)$" $binary_remote_addr;
|
|
34
|
+
"~^POST /api/auth/sso/[a-z0-9-]+/(acs|password)$" $binary_remote_addr;
|
|
35
|
+
"~^POST /api/auth/sso/link/with-(password|code)$" $binary_remote_addr;
|
|
29
36
|
}
|
|
30
37
|
|
|
31
38
|
map "$request_method $uri" $faden_account_key {
|
|
32
39
|
default "";
|
|
33
40
|
"~^POST /api/auth/(forgot-password|reset-password|request-verify-token|verify)$" $binary_remote_addr;
|
|
41
|
+
"~^POST /api/auth/sso/link/code$" $binary_remote_addr;
|
|
42
|
+
"~^POST /api/auth/reset-link/(preview|use)$" $binary_remote_addr;
|
|
34
43
|
"~^POST /api/bootstrap$" $binary_remote_addr;
|
|
35
44
|
}
|
|
36
45
|
|
|
@@ -840,6 +840,11 @@ def deploy_cmd(
|
|
|
840
840
|
env_path, days=chat_retention_days, yes=yes, heading="Step 3f: Deleted chats…",
|
|
841
841
|
)
|
|
842
842
|
|
|
843
|
+
# ── 3g. Add-ons' steps (registry.extend_install_hooks): their settings, secrets, registry login ──
|
|
844
|
+
from faden_cli.core.registry import run_install_hooks # noqa: PLC0415
|
|
845
|
+
|
|
846
|
+
run_install_hooks(shared_dir)
|
|
847
|
+
|
|
843
848
|
# ── 4. Save config ────────────────────────────────────────────
|
|
844
849
|
cfg.install_dir = str(shared_dir)
|
|
845
850
|
cfg.compose_file = compose_file.name
|
|
@@ -386,6 +386,10 @@ def upgrade_cmd(
|
|
|
386
386
|
from faden_cli.commands.tls import refresh_edge_info # noqa: PLC0415
|
|
387
387
|
|
|
388
388
|
refresh_edge_info(shared_dir)
|
|
389
|
+
# Add-ons' steps (registry.extend_install_hooks): their settings, secrets, registry login.
|
|
390
|
+
from faden_cli.core.registry import run_install_hooks # noqa: PLC0415
|
|
391
|
+
|
|
392
|
+
run_install_hooks(shared_dir)
|
|
389
393
|
|
|
390
394
|
# ── 4. Images ─────────────────────────────────────────────
|
|
391
395
|
if release is not None:
|
|
@@ -96,6 +96,16 @@ def unpack(install_dir: Path) -> list[str]:
|
|
|
96
96
|
if not target.is_file() or target.read_bytes() != data:
|
|
97
97
|
target.write_bytes(data)
|
|
98
98
|
written.append(rel)
|
|
99
|
+
# What an add-on ships (registry.extend_bundle_files), replaced like the bundle's own files.
|
|
100
|
+
from faden_cli.core.registry import BUNDLE_FILES # noqa: PLC0415
|
|
101
|
+
|
|
102
|
+
for rel, source in BUNDLE_FILES.items():
|
|
103
|
+
target = install_dir / rel
|
|
104
|
+
data = Path(source).read_bytes()
|
|
105
|
+
if not target.is_file() or target.read_bytes() != data:
|
|
106
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
107
|
+
target.write_bytes(data)
|
|
108
|
+
written.append(rel)
|
|
99
109
|
for rel, content in _STARTING_CONTENT.items():
|
|
100
110
|
target = install_dir / rel
|
|
101
111
|
if not target.exists():
|
|
@@ -121,6 +121,15 @@ class ComposeContext:
|
|
|
121
121
|
error(f"The keys' files could not be written: {exc}")
|
|
122
122
|
sys.exit(1)
|
|
123
123
|
|
|
124
|
+
def extension_overlays(self) -> list[Path]:
|
|
125
|
+
"""The add-ons' compose files beside the main one that exist (``registry.extend_compose_files``)."""
|
|
126
|
+
from faden_cli.core.registry import COMPOSE_OVERLAYS # noqa: PLC0415
|
|
127
|
+
|
|
128
|
+
if not self.compose_files:
|
|
129
|
+
return []
|
|
130
|
+
folder = Path(str(self.compose_files[0])).parent
|
|
131
|
+
return [folder / name for name in COMPOSE_OVERLAYS if (folder / name).is_file()]
|
|
132
|
+
|
|
124
133
|
def base_cmd(self) -> list[str]:
|
|
125
134
|
"""Build the ``docker compose`` prefix with all file/profile flags."""
|
|
126
135
|
docker = shutil.which("docker")
|
|
@@ -135,6 +144,7 @@ class ComposeContext:
|
|
|
135
144
|
overlay = gpu_overlay(self.env_file, self.project_dir)
|
|
136
145
|
if overlay is not None and overlay not in files:
|
|
137
146
|
files.append(overlay)
|
|
147
|
+
files.extend(p for p in self.extension_overlays() if p not in files)
|
|
138
148
|
for f in files:
|
|
139
149
|
cmd.extend(["-f", str(f)])
|
|
140
150
|
if self.env_file and Path(str(self.env_file)).exists():
|
|
@@ -7,8 +7,9 @@ instead of hard-coding values.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
from collections.abc import Callable
|
|
10
11
|
from dataclasses import dataclass
|
|
11
|
-
from pathlib import Path
|
|
12
|
+
from pathlib import Path, PurePosixPath
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
# ── GitHub organisation ───────────────────────────────────────────
|
|
@@ -543,6 +544,67 @@ def extend_dev_processes(extra: list[DevProcess]) -> None:
|
|
|
543
544
|
existing.add(proc.name)
|
|
544
545
|
|
|
545
546
|
|
|
547
|
+
# ── What an add-on adds to an install ────────────────────────────
|
|
548
|
+
|
|
549
|
+
#: Compose files beside the install's ``docker-compose.yaml`` that every compose command of the install
|
|
550
|
+
#: includes, after core's own, when they exist (``compose.ComposeContext.base_cmd``).
|
|
551
|
+
COMPOSE_OVERLAYS: list[str] = []
|
|
552
|
+
#: Files an add-on ships, by their name in the install: ``faden deploy`` and ``faden upgrade`` write them
|
|
553
|
+
#: with the bundled deployment files (``bundle.unpack``), so an upgrade brings the add-on's copy too.
|
|
554
|
+
BUNDLE_FILES: dict[str, Path] = {}
|
|
555
|
+
#: Keys of ``.env`` written to ``secrets/<KEY>`` before every compose command, like core's own keys
|
|
556
|
+
#: (``secret_files.SECRET_FILES``): an add-on's compose file mounts them under ``/run/secrets``.
|
|
557
|
+
SECRET_FILE_KEYS: list[str] = []
|
|
558
|
+
#: Called by ``faden deploy`` and ``faden upgrade`` with the install's directory once its ``.env`` is
|
|
559
|
+
#: written and before any image is pulled or started: an add-on writes its settings and secrets, or logs in
|
|
560
|
+
#: to the registry its images come from. One that raises stops the deploy or the upgrade.
|
|
561
|
+
INSTALL_HOOKS: list[Callable[[Path], None]] = []
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
def extend_compose_files(names: list[str]) -> None:
|
|
565
|
+
"""Add compose files every command of an install includes when they exist (idempotent)."""
|
|
566
|
+
for name in names:
|
|
567
|
+
if Path(name).name != name or not name.endswith((".yaml", ".yml")):
|
|
568
|
+
msg = f"Not a compose file beside the install's own: {name!r}"
|
|
569
|
+
raise ValueError(msg)
|
|
570
|
+
if name not in COMPOSE_OVERLAYS:
|
|
571
|
+
COMPOSE_OVERLAYS.append(name)
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
def extend_bundle_files(files: dict[str, Path]) -> None:
|
|
575
|
+
"""Add files written into an install with the bundled deployment files (a later add wins)."""
|
|
576
|
+
for name, source in files.items():
|
|
577
|
+
# Either separator: an install on Windows reads "/etc/x" as relative to its drive.
|
|
578
|
+
rel = PurePosixPath(name.replace("\\", "/"))
|
|
579
|
+
if rel.is_absolute() or Path(name).is_absolute() or ".." in rel.parts:
|
|
580
|
+
msg = f"Not a path inside the install: {name!r}"
|
|
581
|
+
raise ValueError(msg)
|
|
582
|
+
BUNDLE_FILES[name] = source
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
def extend_secret_files(keys: list[str]) -> None:
|
|
586
|
+
"""Add ``.env`` keys written as files under ``secrets/`` before every compose command (idempotent)."""
|
|
587
|
+
for key in keys:
|
|
588
|
+
if not key.isidentifier() or key.upper() != key:
|
|
589
|
+
msg = f"Not an .env key: {key!r}"
|
|
590
|
+
raise ValueError(msg)
|
|
591
|
+
if key not in SECRET_FILE_KEYS:
|
|
592
|
+
SECRET_FILE_KEYS.append(key)
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def extend_install_hooks(hooks: list[Callable[[Path], None]]) -> None:
|
|
596
|
+
"""Add steps ``faden deploy`` and ``faden upgrade`` run before pulling (idempotent)."""
|
|
597
|
+
for hook in hooks:
|
|
598
|
+
if hook not in INSTALL_HOOKS:
|
|
599
|
+
INSTALL_HOOKS.append(hook)
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
def run_install_hooks(install_dir: Path) -> None:
|
|
603
|
+
"""Run the add-ons' install steps, in the order they were added."""
|
|
604
|
+
for hook in INSTALL_HOOKS:
|
|
605
|
+
hook(install_dir)
|
|
606
|
+
|
|
607
|
+
|
|
546
608
|
def _read_env_values(path: Path) -> dict[str, str]:
|
|
547
609
|
"""Parse a simple ``KEY=VALUE`` env file into a dict."""
|
|
548
610
|
path = Path(path)
|
|
@@ -94,6 +94,12 @@ def generate_definitions(
|
|
|
94
94
|
#: still running and leaves it be (backend ``services/llm/tasks.py``).
|
|
95
95
|
CONSUMER_TIMEOUT_MS = 2 * 24 * 3600 * 1000
|
|
96
96
|
|
|
97
|
+
#: The memory RabbitMQ's container may use (``faden-rmq``'s limit in shared/docker-compose.yaml), which RabbitMQ is
|
|
98
|
+
#: told (``total_memory_available_override_value``): it reads the host's memory otherwise, and its memory alarm
|
|
99
|
+
#: (60 % of what it believes it has: 40 GB on a 64 GB host) never came before the kernel killed it at the
|
|
100
|
+
#: container's limit. Killed at start-up after a reboot, in a loop, while it recovered its data.
|
|
101
|
+
MEMORY_BYTES = 1024**3
|
|
102
|
+
|
|
97
103
|
|
|
98
104
|
def write_definitions(
|
|
99
105
|
output_dir: Path,
|
|
@@ -128,7 +134,8 @@ def write_definitions(
|
|
|
128
134
|
conf_path.write_text(
|
|
129
135
|
"# Auto-generated by faden deploy — do not edit.\n"
|
|
130
136
|
"load_definitions = /etc/rabbitmq/definitions.json\n"
|
|
131
|
-
f"consumer_timeout = {CONSUMER_TIMEOUT_MS}\n"
|
|
137
|
+
f"consumer_timeout = {CONSUMER_TIMEOUT_MS}\n"
|
|
138
|
+
f"total_memory_available_override_value = {MEMORY_BYTES}\n",
|
|
132
139
|
encoding="utf-8",
|
|
133
140
|
)
|
|
134
141
|
|
|
@@ -75,9 +75,11 @@ def _clear_docker_folder(path: Path) -> None:
|
|
|
75
75
|
|
|
76
76
|
def write(project_dir: Path, env_file: Path) -> list[str]:
|
|
77
77
|
"""Write every key's file from *env_file* into ``<project_dir>/secrets/``; the required keys .env lacks."""
|
|
78
|
+
from faden_cli.core.registry import SECRET_FILE_KEYS # noqa: PLC0415 -- an add-on's keys, added at start
|
|
79
|
+
|
|
78
80
|
values = read_env_file(env_file) if env_file.is_file() else {}
|
|
79
81
|
folder = private_dir(project_dir / FOLDER)
|
|
80
|
-
for name in SECRET_FILES:
|
|
82
|
+
for name in (*SECRET_FILES, *(key for key in SECRET_FILE_KEYS if key not in SECRET_FILES)):
|
|
81
83
|
path = folder / name
|
|
82
84
|
if path.is_dir():
|
|
83
85
|
_clear_docker_folder(path)
|
|
@@ -6,7 +6,12 @@ tests drive that code, and three of them mocked only some of the helpers
|
|
|
6
6
|
and killed the development stack the developer was running at the time.
|
|
7
7
|
A test run should never be able to do that. The same went for a deploy step
|
|
8
8
|
that ran `docker exec faden-postgres` for real and switched off logins of the
|
|
9
|
-
developer's database, so a real `docker exec` fails the test too.
|
|
9
|
+
developer's database, so a real `docker exec` fails the test too. And for the
|
|
10
|
+
runtime images `deploy` and `upgrade` fetch at the end: the upgrade tests read
|
|
11
|
+
the pins out of the real backend image and pulled every runtime image it names
|
|
12
|
+
(about 10 GB each), which filled CI's disk; a machine that already had them
|
|
13
|
+
passed. So that step's Docker is answered "not here", and a real `docker pull`
|
|
14
|
+
fails the test.
|
|
10
15
|
|
|
11
16
|
The guard patches the primitives rather than the functions, so the tests that
|
|
12
17
|
exist to exercise the matching logic still call the real thing; they just
|
|
@@ -17,10 +22,12 @@ this fixture and therefore wins.
|
|
|
17
22
|
|
|
18
23
|
from __future__ import annotations
|
|
19
24
|
|
|
25
|
+
import subprocess
|
|
26
|
+
|
|
20
27
|
import pytest
|
|
21
28
|
|
|
22
29
|
from faden_cli.commands.dev import dev_up
|
|
23
|
-
from faden_cli.core import backend_tool, backup, sealing
|
|
30
|
+
from faden_cli.core import backend_tool, backup, runtime_images, sealing
|
|
24
31
|
|
|
25
32
|
|
|
26
33
|
@pytest.fixture(autouse=True)
|
|
@@ -33,6 +40,12 @@ def never_touch_real_processes(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
|
33
40
|
# The upgrade, deploy and rebuild steps that carry chat files out of the gateway container: none here.
|
|
34
41
|
monkeypatch.setattr(backup, "keep_chat_files", lambda *_a, **_k: None)
|
|
35
42
|
monkeypatch.setattr(backup, "keep_company_files", lambda *_a, **_k: None)
|
|
43
|
+
# The runtime images step's Docker (reading the pins out of the backend image, inspecting, pulling): it
|
|
44
|
+
# finds nothing, says so and goes on, as it does on a server without Docker. Its own tests fake `_run`.
|
|
45
|
+
monkeypatch.setattr(
|
|
46
|
+
runtime_images, "_run",
|
|
47
|
+
lambda cmd, **_k: subprocess.CompletedProcess(cmd, 1, "", "no Docker in the tests"),
|
|
48
|
+
)
|
|
36
49
|
|
|
37
50
|
# Nothing to find...
|
|
38
51
|
monkeypatch.setattr(dev_up.psutil, "process_iter", lambda *_a, **_k: [])
|
|
@@ -60,6 +73,11 @@ def never_touch_real_processes(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
|
60
73
|
"cp" in args[1:2] or list(args[1:3]) == ["volume", "create"]
|
|
61
74
|
):
|
|
62
75
|
pytest.fail(f"a test reached a real `docker cp` or `docker volume create`: {args!r}")
|
|
76
|
+
# Nor a pull: the images are gigabytes, and a test passes only where they happen to be present.
|
|
77
|
+
if isinstance(args, (list, tuple)) and isinstance(first, str) and "docker" in first.lower() and (
|
|
78
|
+
"pull" in args[1:2]
|
|
79
|
+
):
|
|
80
|
+
pytest.fail(f"a test reached a real `docker pull`: {args!r}")
|
|
63
81
|
return real_run(args, *rest, **kwargs)
|
|
64
82
|
|
|
65
83
|
monkeypatch.setattr(dev_up.subprocess, "run", guarded_run)
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""What an add-on adds to an install: compose files, files written with the deployment, steps before pulling.
|
|
2
|
+
|
|
3
|
+
``registry.extend_compose_files``: compose files every command includes. ``extend_bundle_files``: files
|
|
4
|
+
written with the deployment. ``extend_install_hooks``: steps run before images are pulled. Without an add-on
|
|
5
|
+
nothing changes.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
|
|
14
|
+
# The release bundle and the stand-ins for Docker an upgrade runs with (tests/test_release_bundle.py).
|
|
15
|
+
from test_release_bundle import _configure, _no_docker, carried # noqa: F401
|
|
16
|
+
|
|
17
|
+
from faden_cli.core import bundle, registry
|
|
18
|
+
from faden_cli.core.compose import ComposeContext
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.fixture(autouse=True)
|
|
22
|
+
def no_addon(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
23
|
+
"""Each test starts without an add-on, and leaves none behind."""
|
|
24
|
+
monkeypatch.setattr(registry, "COMPOSE_OVERLAYS", [])
|
|
25
|
+
monkeypatch.setattr(registry, "BUNDLE_FILES", {})
|
|
26
|
+
monkeypatch.setattr(registry, "INSTALL_HOOKS", [])
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _files(cmd: list[str]) -> list[str]:
|
|
30
|
+
return [Path(cmd[i + 1]).name for i, part in enumerate(cmd) if part == "-f"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _ctx(folder: Path) -> ComposeContext:
|
|
34
|
+
(folder / "docker-compose.yaml").write_text("services: {}\n", encoding="utf-8")
|
|
35
|
+
return ComposeContext(compose_files=[folder / "docker-compose.yaml"], project_dir=folder)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_without_an_addon_the_install_runs_core_alone(tmp_path: Path) -> None:
|
|
39
|
+
(tmp_path / "docker-compose.extra.yaml").write_text("services: {}\n", encoding="utf-8")
|
|
40
|
+
assert _files(_ctx(tmp_path).base_cmd()) == ["docker-compose.yaml"]
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def test_every_command_includes_an_addons_compose_file_after_cores(tmp_path: Path) -> None:
|
|
44
|
+
registry.extend_compose_files(["docker-compose.extra.yaml", "docker-compose.missing.yaml"])
|
|
45
|
+
registry.extend_compose_files(["docker-compose.extra.yaml"])
|
|
46
|
+
(tmp_path / "docker-compose.extra.yaml").write_text("services: {}\n", encoding="utf-8")
|
|
47
|
+
assert _files(_ctx(tmp_path).base_cmd()) == ["docker-compose.yaml", "docker-compose.extra.yaml"]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@pytest.mark.parametrize("name", ["../docker-compose.x.yaml", "sub/extra.yaml", "extra.env", "/etc/x.yaml"])
|
|
51
|
+
def test_only_compose_files_beside_the_install_own(name: str) -> None:
|
|
52
|
+
with pytest.raises(ValueError, match="Not a compose file"):
|
|
53
|
+
registry.extend_compose_files([name])
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def test_unpacking_writes_an_addons_files_and_replaces_them_on_upgrade(
|
|
57
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
|
|
58
|
+
) -> None:
|
|
59
|
+
shipped = tmp_path / "bundle"
|
|
60
|
+
shipped.mkdir()
|
|
61
|
+
(shipped / "docker-compose.yaml").write_text("services: {}\n", encoding="utf-8")
|
|
62
|
+
monkeypatch.setattr(bundle, "bundled_files", lambda: shipped)
|
|
63
|
+
source = tmp_path / "addon.yaml"
|
|
64
|
+
source.write_text("services: {a: {}}\n", encoding="utf-8")
|
|
65
|
+
registry.extend_bundle_files({"docker-compose.extra.yaml": source})
|
|
66
|
+
|
|
67
|
+
install = tmp_path / "install"
|
|
68
|
+
assert "docker-compose.extra.yaml" in bundle.unpack(install)
|
|
69
|
+
assert (install / "docker-compose.extra.yaml").read_text(encoding="utf-8") == "services: {a: {}}\n"
|
|
70
|
+
assert "docker-compose.extra.yaml" not in bundle.unpack(install), "unchanged: not written again"
|
|
71
|
+
source.write_text("services: {b: {}}\n", encoding="utf-8")
|
|
72
|
+
assert "docker-compose.extra.yaml" in bundle.unpack(install), "a newer add-on brings its copy"
|
|
73
|
+
assert (install / "docker-compose.extra.yaml").read_text(encoding="utf-8") == "services: {b: {}}\n"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@pytest.mark.parametrize("name", ["../outside.yaml", "/etc/passwd"])
|
|
77
|
+
def test_an_addons_files_stay_inside_the_install(name: str, tmp_path: Path) -> None:
|
|
78
|
+
with pytest.raises(ValueError, match="Not a path inside the install"):
|
|
79
|
+
registry.extend_bundle_files({name: tmp_path / "x"})
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_install_steps_run_once_each_in_order(tmp_path: Path) -> None:
|
|
83
|
+
seen: list[tuple[str, Path]] = []
|
|
84
|
+
|
|
85
|
+
def first(folder: Path) -> None:
|
|
86
|
+
seen.append(("first", folder))
|
|
87
|
+
|
|
88
|
+
def second(folder: Path) -> None:
|
|
89
|
+
seen.append(("second", folder))
|
|
90
|
+
|
|
91
|
+
registry.extend_install_hooks([first, second])
|
|
92
|
+
registry.extend_install_hooks([first])
|
|
93
|
+
registry.run_install_hooks(tmp_path)
|
|
94
|
+
assert seen == [("first", tmp_path), ("second", tmp_path)]
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def test_a_failing_install_step_stops_what_runs_it(tmp_path: Path) -> None:
|
|
98
|
+
def broken(folder: Path) -> None:
|
|
99
|
+
msg = f"cannot log in for {folder}"
|
|
100
|
+
raise RuntimeError(msg)
|
|
101
|
+
|
|
102
|
+
registry.extend_install_hooks([broken])
|
|
103
|
+
with pytest.raises(RuntimeError, match="cannot log in"):
|
|
104
|
+
registry.run_install_hooks(tmp_path)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def test_an_upgrade_runs_the_steps_after_the_env_and_before_pulling(
|
|
108
|
+
carried: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch, # noqa: F811 -- the fixture imported above
|
|
109
|
+
) -> None:
|
|
110
|
+
import json # noqa: PLC0415
|
|
111
|
+
|
|
112
|
+
from click.testing import CliRunner # noqa: PLC0415
|
|
113
|
+
|
|
114
|
+
from faden_cli.commands import upgrade as upgrade_module # noqa: PLC0415
|
|
115
|
+
from faden_cli.core import update_check # noqa: PLC0415
|
|
116
|
+
|
|
117
|
+
install = tmp_path / "fadenstack"
|
|
118
|
+
bundle.unpack(install)
|
|
119
|
+
(install / bundle.MARKER).write_text(json.dumps({"version": "0.0.1"}), encoding="utf-8")
|
|
120
|
+
(install / ".env").write_text("VERSION=0.0.1\nPOSTGRES_PASSWORD=pgpw\n", encoding="utf-8")
|
|
121
|
+
_configure(tmp_path, monkeypatch, install)
|
|
122
|
+
calls: list[str] = []
|
|
123
|
+
_no_docker(monkeypatch, calls)
|
|
124
|
+
monkeypatch.setenv(update_check.ENV_OFF, "1")
|
|
125
|
+
monkeypatch.setattr(update_check, "cached_feed", lambda: None)
|
|
126
|
+
|
|
127
|
+
def step(folder: Path) -> None:
|
|
128
|
+
env = (folder / ".env").read_text(encoding="utf-8")
|
|
129
|
+
calls.append(f"step:{folder.name}:{'REDIS_AUTH' in env}")
|
|
130
|
+
|
|
131
|
+
registry.extend_install_hooks([step])
|
|
132
|
+
result = CliRunner().invoke(upgrade_module.upgrade_cmd, ["-y", "--no-backup", "--skip-doctor"])
|
|
133
|
+
assert result.exit_code == 0, result.output
|
|
134
|
+
assert calls[:2] == ["step:fadenstack:True", "pull"], "after the .env is complete, before any pull"
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def test_an_addons_keys_are_written_as_files_like_cores(
|
|
138
|
+
tmp_path: Path, monkeypatch: pytest.MonkeyPatch,
|
|
139
|
+
) -> None:
|
|
140
|
+
from faden_cli.core import secret_files # noqa: PLC0415
|
|
141
|
+
|
|
142
|
+
monkeypatch.setattr(registry, "SECRET_FILE_KEYS", [])
|
|
143
|
+
registry.extend_secret_files(["FADEN_ADDON_KEY", "FADEN_ADDON_KEY"])
|
|
144
|
+
(tmp_path / ".env").write_text("FADEN_ADDON_KEY=addon-secret\n", encoding="utf-8")
|
|
145
|
+
secret_files.write(tmp_path, tmp_path / ".env")
|
|
146
|
+
assert (tmp_path / "secrets" / "FADEN_ADDON_KEY").read_text(encoding="utf-8") == "addon-secret"
|
|
147
|
+
written = {p.name for p in (tmp_path / "secrets").iterdir()}
|
|
148
|
+
assert written == {*secret_files.SECRET_FILES, "FADEN_ADDON_KEY"}
|
|
149
|
+
(tmp_path / ".env").write_text("FADEN_ADDON_KEY=rotated\n", encoding="utf-8")
|
|
150
|
+
secret_files.write(tmp_path, tmp_path / ".env")
|
|
151
|
+
rotated = (tmp_path / "secrets" / "FADEN_ADDON_KEY").read_text(encoding="utf-8")
|
|
152
|
+
assert rotated == "rotated", "follows .env"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
@pytest.mark.parametrize("key", ["faden_lower", "FADEN-DASH", "../ESCAPE", ""])
|
|
156
|
+
def test_only_env_keys_become_files(key: str) -> None:
|
|
157
|
+
with pytest.raises(ValueError, match=r"Not an \.env key"):
|
|
158
|
+
registry.extend_secret_files([key])
|
|
@@ -94,6 +94,10 @@ def _no_docker(monkeypatch: pytest.MonkeyPatch, calls: list[str]) -> None:
|
|
|
94
94
|
monkeypatch.setattr(bootstrap, "wait_for_backend", lambda *a, **k: True)
|
|
95
95
|
monkeypatch.setattr(deploy_module, "_sync_postgres_password", lambda *a, **k: None)
|
|
96
96
|
monkeypatch.setattr(deploy_module, "retire_fixed_logins", lambda *a, **k: [])
|
|
97
|
+
# The runtime images are pulled for real otherwise: several gigabytes, on any machine with Docker.
|
|
98
|
+
from faden_cli.commands import runtime_images # noqa: PLC0415
|
|
99
|
+
|
|
100
|
+
monkeypatch.setattr(runtime_images, "run_step", lambda *a, **k: calls.append("runtime images"))
|
|
97
101
|
|
|
98
102
|
|
|
99
103
|
def _configure(tmp_path: Path, monkeypatch: pytest.MonkeyPatch, install: Path) -> None:
|
|
@@ -137,7 +141,7 @@ def test_an_upgrade_moves_a_release_install_to_this_release(
|
|
|
137
141
|
assert "REDIS_AUTH" in env, "keys a release introduces are added"
|
|
138
142
|
assert env["FADEN_BACKEND_BOOTSTRAP_TOKEN"], "an install from before the bootstrap token gets one"
|
|
139
143
|
# Pulled, not built; then the front door restarts first, and the rest.
|
|
140
|
-
assert calls == ["pull", "up", "up"], "published images are pulled, not built"
|
|
144
|
+
assert calls == ["pull", "up", "up", "runtime images"], "published images are pulled, not built"
|
|
141
145
|
assert bundle.installed_release(install) == __version__
|
|
142
146
|
|
|
143
147
|
|
|
@@ -16,7 +16,7 @@ import yaml
|
|
|
16
16
|
|
|
17
17
|
from faden_cli.core import tune
|
|
18
18
|
from faden_cli.core.env_gen import read_env_file, write_env_file
|
|
19
|
-
from faden_cli.core.rmq import CONSUMER_TIMEOUT_MS, write_definitions
|
|
19
|
+
from faden_cli.core.rmq import CONSUMER_TIMEOUT_MS, MEMORY_BYTES, write_definitions
|
|
20
20
|
from faden_cli.core.sysinfo import (
|
|
21
21
|
MAX_DOWNLOAD_RANGES,
|
|
22
22
|
MIN_DOWNLOAD_RANGES,
|
|
@@ -110,3 +110,14 @@ def test_rabbitmq_lets_a_task_hold_its_message_for_as_long_as_a_download(tmp_pat
|
|
|
110
110
|
conf = (tmp_path / "rabbitmq" / "10-faden.conf").read_text(encoding="utf-8")
|
|
111
111
|
assert f"consumer_timeout = {CONSUMER_TIMEOUT_MS}" in conf
|
|
112
112
|
assert CONSUMER_TIMEOUT_MS >= 24 * 3600 * 1000
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_rabbitmq_is_told_the_memory_its_container_has(tmp_path: Path) -> None:
|
|
116
|
+
"""It read the host's (40 GB of a 64 GB machine) and was killed at 512M before its own alarm came."""
|
|
117
|
+
write_definitions(tmp_path, admin_pass="a", backend_pass="b", api_pass="c")
|
|
118
|
+
conf = (tmp_path / "rabbitmq" / "10-faden.conf").read_text(encoding="utf-8")
|
|
119
|
+
assert f"total_memory_available_override_value = {MEMORY_BYTES}" in conf
|
|
120
|
+
rmq = yaml.safe_load(COMPOSE.read_text(encoding="utf-8"))["services"]["faden-rmq"]
|
|
121
|
+
limit = rmq["deploy"]["resources"]["limits"]["memory"]
|
|
122
|
+
assert limit.endswith("G") and int(limit[:-1]) * 1024**3 == MEMORY_BYTES, "Docker's G is GiB"
|
|
123
|
+
assert MEMORY_BYTES >= 1024**3
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/agent-binaries/.gitignore
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/docker-compose.gpu.yaml
RENAMED
|
File without changes
|
{fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/docker-compose.pii-gpu.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/nginx/security-headers.conf
RENAMED
|
File without changes
|
|
File without changes
|
{fadenstack-0.5.0rc4 → fadenstack-0.5.1}/src/faden_cli/bundle/shared/prometheus/prometheus.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|