ignition-stack 0.5.0__tar.gz → 0.6.0__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.
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/PKG-INFO +7 -5
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/README.md +6 -4
- ignition_stack-0.6.0/ignition_stack/__init__.py +1 -0
- ignition_stack-0.6.0/ignition_stack/architectures/__init__.py +35 -0
- {ignition_stack-0.5.0/ignition_stack/profiles → ignition_stack-0.6.0/ignition_stack/architectures}/advisory.py +1 -1
- {ignition_stack-0.5.0/ignition_stack/profiles → ignition_stack-0.6.0/ignition_stack/architectures}/base.py +68 -64
- ignition_stack-0.6.0/ignition_stack/architectures/basic.py +45 -0
- {ignition_stack-0.5.0/ignition_stack/profiles → ignition_stack-0.6.0/ignition_stack/architectures}/carry.py +27 -27
- {ignition_stack-0.5.0/ignition_stack/profiles → ignition_stack-0.6.0/ignition_stack/architectures}/hub_and_spoke.py +23 -23
- ignition_stack-0.5.0/ignition_stack/profiles/scaleout.py → ignition_stack-0.6.0/ignition_stack/architectures/scale_out.py +18 -17
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/catalog/builtins.py +3 -3
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/cli.py +130 -98
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/completion.py +21 -20
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/engine.py +82 -2
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/templates/footer.yaml.j2 +5 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/templates/services/ignition.yaml.j2 +12 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/writer.py +13 -5
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/config/io.py +1 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/config/schema.py +101 -19
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/lifecycle/record.py +3 -3
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/lifecycle/regenerate.py +1 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/postsetup/generator.py +171 -11
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/services/manifest.py +79 -2
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/services/resolver.py +58 -31
- ignition_stack-0.6.0/ignition_stack/templates/post-setup/connections.md.j2 +23 -0
- ignition_stack-0.6.0/ignition_stack/templates/post-setup/reverse-proxy.md.j2 +17 -0
- ignition_stack-0.6.0/ignition_stack/templates/services/chariot/manifest.yaml +48 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/emqx/manifest.yaml +10 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/hivemq/manifest.yaml +9 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/kafka/manifest.yaml +8 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/keycloak/manifest.yaml +10 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mariadb/manifest.yaml +10 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/modbus-sim/manifest.yaml +6 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mongo/manifest.yaml +10 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mysql/manifest.yaml +10 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/n8n/manifest.yaml +6 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/opcua-sim/manifest.yaml +6 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/manifest.yaml +12 -1
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/rabbitmq/manifest.yaml +11 -1
- ignition_stack-0.6.0/ignition_stack/wizard.py +1054 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/wizard_composer.py +246 -71
- ignition_stack-0.5.0/tests/test_profiles.py → ignition_stack-0.6.0/tests/test_architectures.py +325 -214
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_completion.py +8 -8
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_declarative_io.py +83 -38
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_disable_builtins.py +15 -15
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_docs_cli_reference.py +5 -6
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_iiot_overlay.py +23 -23
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_init_standalone.py +8 -8
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_lifecycle.py +14 -14
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_manifest_invariants.py +57 -13
- ignition_stack-0.6.0/tests/test_postsetup.py +279 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_redundancy.py +21 -21
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_registry.py +8 -6
- ignition_stack-0.5.0/tests/test_switch_profile_registry.py → ignition_stack-0.6.0/tests/test_switch_arch_registry.py +52 -27
- ignition_stack-0.6.0/tests/test_wizard_back_nav.py +338 -0
- ignition_stack-0.6.0/tests/test_wizard_breadcrumb.py +188 -0
- ignition_stack-0.6.0/tests/test_wizard_composer.py +747 -0
- ignition_stack-0.5.0/ignition_stack/__init__.py +0 -1
- ignition_stack-0.5.0/ignition_stack/profiles/__init__.py +0 -33
- ignition_stack-0.5.0/ignition_stack/profiles/mcp_n8n.py +0 -55
- ignition_stack-0.5.0/ignition_stack/profiles/standalone.py +0 -44
- ignition_stack-0.5.0/ignition_stack/templates/post-setup/reverse-proxy.md.j2 +0 -8
- ignition_stack-0.5.0/ignition_stack/templates/services/chariot/manifest.yaml +0 -30
- ignition_stack-0.5.0/ignition_stack/wizard.py +0 -634
- ignition_stack-0.5.0/tests/golden/profiles/mcp-n8n/docker-compose.yaml +0 -72
- ignition_stack-0.5.0/tests/test_postsetup.py +0 -156
- ignition_stack-0.5.0/tests/test_wizard_composer.py +0 -448
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/.gitignore +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/LICENSE +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/builtin_modules.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/catalog/__init__.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/catalog/download.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/catalog/loader.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/catalog/schema.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/catalog/verify.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/commands/__init__.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/commands/modules.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/__init__.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/templates/header.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/compose/templates/services/bootstrap.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/config/__init__.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/lifecycle/__init__.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/lifecycle/cleanup.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/postsetup/__init__.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/services/__init__.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/services/loader.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/__init__.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/default-namespace/Sparkplug B/config.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/default-namespace/Sparkplug B/resource.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/general/config.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/general/resource.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/namespace-server-set/Sparkplug B-Default Set/config.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/namespace-server-set/Sparkplug B-Default Set/resource.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/server/Chariot SCADA/config.json.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/server/Chariot SCADA/resource.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/server-set/Default Set/config.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-engine/config/resources/core/com.cirruslink.mqtt.engine.gateway/server-set/Default Set/resource.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/general/config.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/general/resource.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/history-store/Default In-Memory Store/config.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/history-store/Default In-Memory Store/resource.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/server/Chariot SCADA/config.json.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/server/Chariot SCADA/resource.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/server-set/Default/config.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/server-set/Default/resource.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/transmitter/{{gateway}}/config.json.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/iiot/gateway-resources-mqtt-transmission/config/resources/core/com.cirruslink.mqtt.transmission.gateway/transmitter/{{gateway}}/resource.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/_default.md.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/device-connection.md.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/gateway-network-link.md.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/identity-provider.md.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/kafka-connector.md.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/mcp-module.md.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/mqtt-engine-connection.md.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/opc-ua-connection.md.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/redundancy-pairing.md.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/redundancy/redundancy.xml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/chariot/compose.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/chariot/seed/service/USAGE.md +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/chariot/seed/service/chariot-trial.sh +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/emqx/compose.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/emqx/seed/service/USAGE.md +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/hivemq/compose.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/hivemq/seed/service/USAGE.md +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/kafka/compose.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/kafka/seed/service/USAGE.md +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/keycloak/compose.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/keycloak/seed/gateway-resources/config/resources/core/ignition/identity-provider/keycloak/config.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/keycloak/seed/gateway-resources/config/resources/core/ignition/identity-provider/keycloak/resource.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/keycloak/seed/service/import/ignition-realm.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mariadb/compose.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mariadb/seed/service/initdb/00-create-extra-databases.sh +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/modbus-sim/compose.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/modbus-sim/seed/service/USAGE.md +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mongo/compose.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mongo/seed/service/initdb/01-demo-collection.js +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mysql/compose.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mysql/seed/service/initdb/00-create-extra-databases.sh +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/n8n/compose.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/n8n/seed/service/USAGE.md +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/opcua-sim/compose.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/opcua-sim/seed/service/USAGE.md +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/compose.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/database-connection/db/config.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/database-connection/db/resource.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/secret-provider/internal-secret-provider/config.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/secret-provider/internal-secret-provider/resource.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/seed/service/initdb/00-create-extra-databases.sh +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/rabbitmq/compose.yaml.j2 +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/rabbitmq/seed/service/enabled_plugins +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/scripts/docker-bootstrap.sh +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/services/ignition/config/resources/core/config-mode.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/services/ignition/config/resources/dev/config-mode.json +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/services/ignition/projects/.gitkeep +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/ignition_stack/update_check.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/modules.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/pyproject.toml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/__init__.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/conftest.py +0 -0
- {ignition_stack-0.5.0/tests/golden/profiles/standalone → ignition_stack-0.6.0/tests/golden/architectures/basic}/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0/tests/golden/profiles → ignition_stack-0.6.0/tests/golden/architectures}/hub-and-spoke/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0/tests/golden/profiles/scaleout → ignition_stack-0.6.0/tests/golden/architectures/scale-out}/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0/tests/golden/profiles/scaleout-redundant → ignition_stack-0.6.0/tests/golden/architectures/scale-out-redundant}/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/combos/hub-and-spoke-iiot-chariot/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/combos/network-split/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/combos/smoke-stack/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/scaleout-skeleton/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/chariot/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/db-mariadb/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/db-mongo/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/db-mysql/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/db-postgres/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/emqx/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/hivemq/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/kafka/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/keycloak/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/modbus-sim/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/n8n/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/opcua-sim/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/services/rabbitmq/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/golden/standalone-postgres/docker-compose.yaml +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_builtin_catalog_smoke.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_compose_engine.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_modules_catalog.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_modules_cli.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_service_catalog.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_service_catalog_smoke.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/tests/test_update_check.py +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/verification/iiot-spike/README.md +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/verification/redundancy-spike/README.md +0 -0
- {ignition_stack-0.5.0 → ignition_stack-0.6.0}/verification/smoke/README.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ignition-stack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: CLI that generates ready-to-run Docker Compose stacks for Ignition 8.3 SCADA demos and SE engagements
|
|
5
5
|
Author-email: Eric Knorr <etknorr@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -33,7 +33,7 @@ Description-Content-Type: text/markdown
|
|
|
33
33
|
[](https://github.com/ia-eknorr/ignition-stack/actions/workflows/docs.yml)
|
|
34
34
|
[](https://ia-eknorr.github.io/ignition-stack/)
|
|
35
35
|
|
|
36
|
-
CLI that generates ready-to-run Docker Compose stacks for Ignition 8.3 SCADA demos and SE engagements.
|
|
36
|
+
CLI that generates ready-to-run Docker Compose stacks for Ignition 8.3 SCADA demos and SE engagements. Pick a system architecture (`basic`, `scale-out`, or `hub-and-spoke`), layer on services, and it writes a self-contained project: a hand-readable compose file, `.env`, file-config seed resources, and a `POST-SETUP.md` listing only what could not be pre-seeded.
|
|
37
37
|
|
|
38
38
|
See [`docs/docs/reference/seeding-matrix.md`](docs/docs/reference/seeding-matrix.md) for which Ignition 8.3 connection types can be provisioned from the filesystem and env on a live 8.3.6 gateway. Full documentation lives in the [`docs/`](docs/) Docusaurus site.
|
|
39
39
|
|
|
@@ -55,23 +55,25 @@ pipx install git+https://github.com/ia-eknorr/ignition-stack.git@<branch>
|
|
|
55
55
|
Generate a project and bring it up:
|
|
56
56
|
|
|
57
57
|
```sh
|
|
58
|
-
ignition-stack init demo
|
|
58
|
+
ignition-stack init demo --arch basic
|
|
59
59
|
cd demo
|
|
60
60
|
docker compose up -d
|
|
61
61
|
```
|
|
62
62
|
|
|
63
63
|
The gateway reaches RUNNING with no UI prompts. The admin user is `admin / password` and the gateway is at `http://localhost:9088`. The default Postgres credentials are `ignition / ignition` on the `db` service.
|
|
64
64
|
|
|
65
|
+
Run `init` without `--arch` to walk the interactive wizard instead: it opens architecture-first, then layers database, edition, IIoT, services, and exposure on top, with a summary you can preview, tweak, or generate.
|
|
66
|
+
|
|
65
67
|
Everything that ships in the generated project is hand-readable: `docker-compose.yaml`, `.env`, `scripts/docker-bootstrap.sh`, and a `services/ignition/` resources tree the gateway reads on first boot.
|
|
66
68
|
|
|
67
69
|
## Commands
|
|
68
70
|
|
|
69
71
|
| Command | What it does |
|
|
70
72
|
| --- | --- |
|
|
71
|
-
| `init <name>` | Generate a project at `./<name>/` from
|
|
73
|
+
| `init <name>` | Generate a project at `./<name>/` from an architecture and a few prompts. |
|
|
72
74
|
| `modules` | Download, verify, and manage the `.modl` / JDBC catalog. |
|
|
73
75
|
| `reset` | Re-run generation from an SE-demo project's recorded config. |
|
|
74
|
-
| `switch-
|
|
76
|
+
| `switch-arch` | Reshape an SE-demo project under a different architecture. |
|
|
75
77
|
| `wipe` | Remove this project's containers and volumes only. |
|
|
76
78
|
|
|
77
79
|
See the [CLI reference](docs/docs/reference/cli.md) for every command, argument, and option.
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://github.com/ia-eknorr/ignition-stack/actions/workflows/docs.yml)
|
|
5
5
|
[](https://ia-eknorr.github.io/ignition-stack/)
|
|
6
6
|
|
|
7
|
-
CLI that generates ready-to-run Docker Compose stacks for Ignition 8.3 SCADA demos and SE engagements.
|
|
7
|
+
CLI that generates ready-to-run Docker Compose stacks for Ignition 8.3 SCADA demos and SE engagements. Pick a system architecture (`basic`, `scale-out`, or `hub-and-spoke`), layer on services, and it writes a self-contained project: a hand-readable compose file, `.env`, file-config seed resources, and a `POST-SETUP.md` listing only what could not be pre-seeded.
|
|
8
8
|
|
|
9
9
|
See [`docs/docs/reference/seeding-matrix.md`](docs/docs/reference/seeding-matrix.md) for which Ignition 8.3 connection types can be provisioned from the filesystem and env on a live 8.3.6 gateway. Full documentation lives in the [`docs/`](docs/) Docusaurus site.
|
|
10
10
|
|
|
@@ -26,23 +26,25 @@ pipx install git+https://github.com/ia-eknorr/ignition-stack.git@<branch>
|
|
|
26
26
|
Generate a project and bring it up:
|
|
27
27
|
|
|
28
28
|
```sh
|
|
29
|
-
ignition-stack init demo
|
|
29
|
+
ignition-stack init demo --arch basic
|
|
30
30
|
cd demo
|
|
31
31
|
docker compose up -d
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
The gateway reaches RUNNING with no UI prompts. The admin user is `admin / password` and the gateway is at `http://localhost:9088`. The default Postgres credentials are `ignition / ignition` on the `db` service.
|
|
35
35
|
|
|
36
|
+
Run `init` without `--arch` to walk the interactive wizard instead: it opens architecture-first, then layers database, edition, IIoT, services, and exposure on top, with a summary you can preview, tweak, or generate.
|
|
37
|
+
|
|
36
38
|
Everything that ships in the generated project is hand-readable: `docker-compose.yaml`, `.env`, `scripts/docker-bootstrap.sh`, and a `services/ignition/` resources tree the gateway reads on first boot.
|
|
37
39
|
|
|
38
40
|
## Commands
|
|
39
41
|
|
|
40
42
|
| Command | What it does |
|
|
41
43
|
| --- | --- |
|
|
42
|
-
| `init <name>` | Generate a project at `./<name>/` from
|
|
44
|
+
| `init <name>` | Generate a project at `./<name>/` from an architecture and a few prompts. |
|
|
43
45
|
| `modules` | Download, verify, and manage the `.modl` / JDBC catalog. |
|
|
44
46
|
| `reset` | Re-run generation from an SE-demo project's recorded config. |
|
|
45
|
-
| `switch-
|
|
47
|
+
| `switch-arch` | Reshape an SE-demo project under a different architecture. |
|
|
46
48
|
| `wipe` | Remove this project's containers and volumes only. |
|
|
47
49
|
|
|
48
50
|
See the [CLI reference](docs/docs/reference/cli.md) for every command, argument, and option.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.6.0"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""System architectures: pre-canned shapes that turn intent into config.
|
|
2
|
+
|
|
3
|
+
The slugs mirror Ignition's documented system architectures (Basic / Scale Out
|
|
4
|
+
/ Hub and Spoke). Importing this package registers every built-in architecture
|
|
5
|
+
by side-effect so ``get_architecture("scale-out")`` works without explicit
|
|
6
|
+
module imports.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from ignition_stack.architectures import basic, hub_and_spoke, scale_out # noqa: F401
|
|
10
|
+
from ignition_stack.architectures.advisory import Advisory, spoke_advisory
|
|
11
|
+
from ignition_stack.architectures.base import (
|
|
12
|
+
Architecture,
|
|
13
|
+
ArchOptions,
|
|
14
|
+
apply_iiot,
|
|
15
|
+
build_architecture,
|
|
16
|
+
can_host_redundant_role,
|
|
17
|
+
get_architecture,
|
|
18
|
+
list_architectures,
|
|
19
|
+
mark_redundant,
|
|
20
|
+
)
|
|
21
|
+
from ignition_stack.architectures.hub_and_spoke import ArchitectureError
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"Advisory",
|
|
25
|
+
"ArchOptions",
|
|
26
|
+
"Architecture",
|
|
27
|
+
"ArchitectureError",
|
|
28
|
+
"apply_iiot",
|
|
29
|
+
"build_architecture",
|
|
30
|
+
"can_host_redundant_role",
|
|
31
|
+
"get_architecture",
|
|
32
|
+
"list_architectures",
|
|
33
|
+
"mark_redundant",
|
|
34
|
+
"spoke_advisory",
|
|
35
|
+
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Hub-and-spoke RAM advisory.
|
|
2
2
|
|
|
3
|
-
The hub-and-spoke
|
|
3
|
+
The hub-and-spoke architecture spins up one hub gateway plus *N* spokes; each
|
|
4
4
|
Ignition gateway needs ~1.5 GB to run comfortably. The advisory turns that
|
|
5
5
|
math into a proportional friction signal so SEs can see the cost of a
|
|
6
6
|
large demo without being silently blocked when they really do want it:
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Architecture contract + options + registry.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
intent ("
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
An *architecture* is the small piece of code that turns the user's high-level
|
|
4
|
+
intent ("scale-out", "hub-and-spoke with 3 spokes") into a fully-formed
|
|
5
|
+
:class:`ProjectConfig`. The slugs mirror Ignition's own documented system
|
|
6
|
+
architectures (Basic / Scale Out / Hub and Spoke). The compose engine and the
|
|
7
|
+
dependency resolver are architecture-agnostic; architectures only shape the
|
|
8
|
+
inputs they take.
|
|
7
9
|
|
|
8
10
|
Two-stage pipeline:
|
|
9
11
|
|
|
10
|
-
1. Either the CLI flags or the wizard answers populate
|
|
11
|
-
:class:`
|
|
12
|
-
2. ``
|
|
13
|
-
its ``build()`` method, returning a ``ProjectConfig`` that
|
|
12
|
+
1. Either the CLI flags or the wizard answers populate an
|
|
13
|
+
:class:`ArchOptions` and pick an architecture slug.
|
|
14
|
+
2. ``build_architecture(slug, name, options)`` looks up the architecture and
|
|
15
|
+
calls its ``build()`` method, returning a ``ProjectConfig`` that
|
|
14
16
|
``services.resolver.resolve()`` then expands the usual implicit deps on.
|
|
15
17
|
|
|
16
|
-
Each
|
|
18
|
+
Each architecture is a small dataclass with three pieces:
|
|
17
19
|
|
|
18
20
|
- ``slug`` - the wizard/flag value users type.
|
|
19
21
|
- ``summary`` - one-line description for the wizard menu + docs.
|
|
@@ -39,8 +41,8 @@ from ignition_stack.config import (
|
|
|
39
41
|
|
|
40
42
|
|
|
41
43
|
@dataclass(frozen=True)
|
|
42
|
-
class
|
|
43
|
-
"""Inputs each
|
|
44
|
+
class ArchOptions:
|
|
45
|
+
"""Inputs each architecture reads to shape the resolved config.
|
|
44
46
|
|
|
45
47
|
Every field has a sensible default so callers only set what they
|
|
46
48
|
actually care about. The wizard fills in many of these from prompts;
|
|
@@ -49,10 +51,10 @@ class ProfileOptions:
|
|
|
49
51
|
"""
|
|
50
52
|
|
|
51
53
|
spokes: int = 3
|
|
52
|
-
"""Hub-and-spoke spoke count. Ignored by other
|
|
54
|
+
"""Hub-and-spoke spoke count. Ignored by other architectures."""
|
|
53
55
|
|
|
54
56
|
frontends: int = 1
|
|
55
|
-
"""
|
|
57
|
+
"""Scale-out frontend gateway count. Ignored by other architectures.
|
|
56
58
|
|
|
57
59
|
1 yields a single gateway named ``frontend``; N>1 yields
|
|
58
60
|
``frontend-1``..``frontend-N``. A ``backend`` gateway is always added
|
|
@@ -65,16 +67,16 @@ class ProfileOptions:
|
|
|
65
67
|
network_split: bool | None = None
|
|
66
68
|
"""Tri-state override for the frontend/backend network split.
|
|
67
69
|
|
|
68
|
-
``None`` lets each
|
|
70
|
+
``None`` lets each architecture apply its own default (scale-out splits,
|
|
69
71
|
hub-and-spoke does not). ``True``/``False`` force the split on or off
|
|
70
|
-
regardless of the
|
|
72
|
+
regardless of the architecture default.
|
|
71
73
|
"""
|
|
72
74
|
|
|
73
75
|
edge_role: str | None = None
|
|
74
76
|
"""Which gateway role (if any) runs the Edge edition.
|
|
75
77
|
|
|
76
|
-
For
|
|
77
|
-
be 'spoke' (every spoke runs Edge) or None. The
|
|
78
|
+
For scale-out this is typically 'frontend'; for hub-and-spoke it can
|
|
79
|
+
be 'spoke' (every spoke runs Edge) or None. The architecture is free to
|
|
78
80
|
apply its own default when this is None.
|
|
79
81
|
"""
|
|
80
82
|
|
|
@@ -85,26 +87,27 @@ class ProfileOptions:
|
|
|
85
87
|
"""SQL database for the stack. None = no database (gateway-only)."""
|
|
86
88
|
|
|
87
89
|
services: tuple[str, ...] = ()
|
|
88
|
-
"""Additional service catalog slugs the user picked beyond
|
|
90
|
+
"""Additional service catalog slugs the user picked beyond architecture defaults."""
|
|
89
91
|
|
|
90
92
|
redundant_role: str | None = None
|
|
91
93
|
"""Role (or gateway name) to make redundant, expanding it into a master +
|
|
92
94
|
backup pair. ``None`` (default) builds no redundancy. Must name a singleton
|
|
93
|
-
workhorse role (
|
|
95
|
+
workhorse role (scale-out 'backend', hub-and-spoke 'hub', basic
|
|
94
96
|
'gateway'); replicated tiers ('frontend', 'spoke') are rejected."""
|
|
95
97
|
|
|
96
98
|
disable_builtins: tuple[str, ...] = ()
|
|
97
99
|
"""Built-in module slugs to turn off on every gateway in the stack.
|
|
98
100
|
|
|
99
101
|
Empty (default) leaves all built-ins on. Applied uniformly by
|
|
100
|
-
``
|
|
101
|
-
per-gateway disabling stays a declarative-config-only feature. Slugs
|
|
102
|
-
validated against builtin_modules.yaml by ``GatewayConfig``."""
|
|
102
|
+
``build_architecture`` - the demo intent is "drop Vision/SFC everywhere",
|
|
103
|
+
and per-gateway disabling stays a declarative-config-only feature. Slugs
|
|
104
|
+
are validated against builtin_modules.yaml by ``GatewayConfig``."""
|
|
103
105
|
|
|
104
106
|
iiot: bool = False
|
|
105
107
|
"""Overlay an MQTT/Sparkplug IIoT pipeline (a broker + Cirrus Link
|
|
106
|
-
Transmission/Engine) onto the stack. Off by default; ``
|
|
107
|
-
:func:`apply_iiot` when this is set, defaulting the broker to
|
|
108
|
+
Transmission/Engine) onto the stack. Off by default; ``build_architecture``
|
|
109
|
+
calls :func:`apply_iiot` when this is set, defaulting the broker to
|
|
110
|
+
``chariot``."""
|
|
108
111
|
|
|
109
112
|
iiot_broker: str | None = None
|
|
110
113
|
"""MQTT broker slug the IIoT overlay wires to. ``None`` with ``iiot`` on
|
|
@@ -112,41 +115,41 @@ class ProfileOptions:
|
|
|
112
115
|
when ``iiot`` is False. Validated against the catalog by :func:`apply_iiot`."""
|
|
113
116
|
|
|
114
117
|
|
|
115
|
-
class
|
|
116
|
-
"""A factory that turns ``
|
|
118
|
+
class Architecture(Protocol):
|
|
119
|
+
"""A factory that turns ``ArchOptions`` into a ``ProjectConfig``."""
|
|
117
120
|
|
|
118
121
|
slug: str
|
|
119
122
|
summary: str
|
|
120
123
|
|
|
121
|
-
def build(self, name: str, options:
|
|
124
|
+
def build(self, name: str, options: ArchOptions) -> ProjectConfig: ...
|
|
122
125
|
|
|
123
126
|
|
|
124
|
-
# Registry populated by the
|
|
125
|
-
# insertion-order for stable wizard menus + --help listings.
|
|
126
|
-
_REGISTRY: dict[str,
|
|
127
|
+
# Registry populated by the architecture modules at import time. Keep
|
|
128
|
+
# alphabetical insertion-order for stable wizard menus + --help listings.
|
|
129
|
+
_REGISTRY: dict[str, Architecture] = {}
|
|
127
130
|
|
|
128
131
|
|
|
129
|
-
def register(
|
|
130
|
-
"""Register
|
|
131
|
-
can write ``
|
|
132
|
+
def register(architecture: Architecture) -> Architecture:
|
|
133
|
+
"""Register an architecture by slug. Returns the architecture so module-level
|
|
134
|
+
uses can write ``basic = register(BasicArchitecture())``.
|
|
132
135
|
"""
|
|
133
|
-
if
|
|
134
|
-
raise ValueError(f"
|
|
135
|
-
_REGISTRY[
|
|
136
|
-
return
|
|
136
|
+
if architecture.slug in _REGISTRY:
|
|
137
|
+
raise ValueError(f"architecture '{architecture.slug}' is already registered")
|
|
138
|
+
_REGISTRY[architecture.slug] = architecture
|
|
139
|
+
return architecture
|
|
137
140
|
|
|
138
141
|
|
|
139
|
-
def
|
|
140
|
-
"""Look up a registered
|
|
142
|
+
def get_architecture(slug: str) -> Architecture:
|
|
143
|
+
"""Look up a registered architecture by slug. Raises ``KeyError`` if unknown."""
|
|
141
144
|
try:
|
|
142
145
|
return _REGISTRY[slug]
|
|
143
146
|
except KeyError as exc:
|
|
144
147
|
known = ", ".join(sorted(_REGISTRY))
|
|
145
|
-
raise KeyError(f"unknown
|
|
148
|
+
raise KeyError(f"unknown architecture '{slug}'; known architectures: {known}") from exc
|
|
146
149
|
|
|
147
150
|
|
|
148
|
-
def
|
|
149
|
-
"""All registered
|
|
151
|
+
def list_architectures() -> list[Architecture]:
|
|
152
|
+
"""All registered architectures in stable insertion order."""
|
|
150
153
|
return list(_REGISTRY.values())
|
|
151
154
|
|
|
152
155
|
|
|
@@ -170,9 +173,9 @@ def _matching_gateways(config: ProjectConfig, redundant_role: str) -> list:
|
|
|
170
173
|
def can_host_redundant_role(config: ProjectConfig, redundant_role: str) -> bool:
|
|
171
174
|
"""True when ``config`` has exactly one gateway that can be paired as master.
|
|
172
175
|
|
|
173
|
-
``switch-
|
|
174
|
-
|
|
175
|
-
:func:`mark_redundant` does -
|
|
176
|
+
``switch-arch`` uses this to decide whether redundancy intent recovered from
|
|
177
|
+
the old stack can carry to the target architecture, without raising the way
|
|
178
|
+
:func:`mark_redundant` does - an architecture-specific role (e.g. basic's
|
|
176
179
|
``gateway``) simply may not exist in the destination.
|
|
177
180
|
"""
|
|
178
181
|
if redundant_role in _NON_REDUNDANT_ROLES:
|
|
@@ -186,7 +189,7 @@ def mark_redundant(config: ProjectConfig, redundant_role: str | None) -> Project
|
|
|
186
189
|
Returns ``config`` unchanged when ``redundant_role`` is None. The expansion
|
|
187
190
|
into a master+backup pair happens later in
|
|
188
191
|
:func:`ignition_stack.services.resolver.resolve`; this only marks which
|
|
189
|
-
gateway to pair, so the same logic serves every
|
|
192
|
+
gateway to pair, so the same logic serves every architecture and the wizard.
|
|
190
193
|
|
|
191
194
|
Raises ``ValueError`` (surfaced by the CLI as a usage error, exit code 2)
|
|
192
195
|
when the role is a replicated frontend/spoke tier, unknown, or ambiguous
|
|
@@ -197,7 +200,7 @@ def mark_redundant(config: ProjectConfig, redundant_role: str | None) -> Project
|
|
|
197
200
|
if redundant_role in _NON_REDUNDANT_ROLES:
|
|
198
201
|
raise ValueError(
|
|
199
202
|
f"role '{redundant_role}' is horizontally replicated, not paired; "
|
|
200
|
-
"redundancy applies to a single gateway (e.g. a
|
|
203
|
+
"redundancy applies to a single gateway (e.g. a scale-out 'backend' "
|
|
201
204
|
"or a hub-and-spoke 'hub'), never to frontends or spokes"
|
|
202
205
|
)
|
|
203
206
|
matches = _matching_gateways(config, redundant_role)
|
|
@@ -211,15 +214,16 @@ def mark_redundant(config: ProjectConfig, redundant_role: str | None) -> Project
|
|
|
211
214
|
return config
|
|
212
215
|
|
|
213
216
|
|
|
214
|
-
def
|
|
215
|
-
"""Materialize a ``ProjectConfig`` for the named
|
|
217
|
+
def build_architecture(slug: str, name: str, options: ArchOptions) -> ProjectConfig:
|
|
218
|
+
"""Materialize a ``ProjectConfig`` for the named architecture.
|
|
216
219
|
|
|
217
|
-
The
|
|
218
|
-
redundancy master when ``options.redundant_role`` is set, leaving the
|
|
219
|
-
resolver to expand the pair. Keeping the stamp here (not in each
|
|
220
|
-
means one eligibility rule serves every
|
|
220
|
+
The architecture builds the base topology; ``mark_redundant`` then stamps
|
|
221
|
+
the redundancy master when ``options.redundant_role`` is set, leaving the
|
|
222
|
+
resolver to expand the pair. Keeping the stamp here (not in each
|
|
223
|
+
architecture) means one eligibility rule serves every architecture and the
|
|
224
|
+
wizard alike.
|
|
221
225
|
"""
|
|
222
|
-
config =
|
|
226
|
+
config = get_architecture(slug).build(name, options)
|
|
223
227
|
config = mark_redundant(config, options.redundant_role)
|
|
224
228
|
config = apply_disable_builtins(config, options.disable_builtins)
|
|
225
229
|
broker = (options.iiot_broker or _IIOT_DEFAULT_BROKER) if options.iiot else None
|
|
@@ -230,9 +234,9 @@ def apply_disable_builtins(config: ProjectConfig, disable_builtins: tuple[str, .
|
|
|
230
234
|
"""Stamp ``disable_builtins`` onto every gateway in ``config``.
|
|
231
235
|
|
|
232
236
|
Applied centrally (like :func:`mark_redundant`) so one rule serves every
|
|
233
|
-
|
|
234
|
-
a module everywhere, and a redundant pair must agree on its module set.
|
|
235
|
-
resolver later copies the list onto any expanded backup node.
|
|
237
|
+
architecture and the wizard. Uniform across gateways: the demo intent is to
|
|
238
|
+
drop a module everywhere, and a redundant pair must agree on its module set.
|
|
239
|
+
The resolver later copies the list onto any expanded backup node.
|
|
236
240
|
"""
|
|
237
241
|
if not disable_builtins:
|
|
238
242
|
return config
|
|
@@ -254,9 +258,9 @@ def apply_disable_builtins(config: ProjectConfig, disable_builtins: tuple[str, .
|
|
|
254
258
|
_IIOT_DEFAULT_BROKER = "chariot"
|
|
255
259
|
|
|
256
260
|
# Gateway roles that run MQTT Transmission (edge-side: publish Sparkplug to the
|
|
257
|
-
# broker) versus MQTT Engine (central: subscribe and aggregate). A
|
|
258
|
-
#
|
|
259
|
-
#
|
|
261
|
+
# broker) versus MQTT Engine (central: subscribe and aggregate). A basic shape
|
|
262
|
+
# has neither role, so its single gateway runs both for a self-contained demo
|
|
263
|
+
# loop through the broker.
|
|
260
264
|
_TRANSMISSION_ROLES = frozenset({"spoke", "frontend"})
|
|
261
265
|
_ENGINE_ROLES = frozenset({"hub", "backend"})
|
|
262
266
|
|
|
@@ -273,9 +277,9 @@ def apply_iiot(config: ProjectConfig, broker: str | None) -> ProjectConfig:
|
|
|
273
277
|
plus the Transmission module (they publish Sparkplug to the broker);
|
|
274
278
|
- ``hub`` / ``backend`` gateways get an ``mqtt-engine`` attachment plus the
|
|
275
279
|
Engine module (they subscribe and aggregate);
|
|
276
|
-
- if NO gateway carries any of those roles (
|
|
277
|
-
|
|
278
|
-
|
|
280
|
+
- if NO gateway carries any of those roles (a basic shape), the single/first
|
|
281
|
+
gateway gets BOTH attachments + both modules - a self-contained demo loop
|
|
282
|
+
through the broker.
|
|
279
283
|
|
|
280
284
|
Brokers are not ``never_on_edge``, so an Edge spoke attaching with role
|
|
281
285
|
``mqtt-transmission`` is correct and expected. Idempotent: guards on
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Basic architecture: one full Ignition gateway + optional SQL DB.
|
|
2
|
+
|
|
3
|
+
This mirrors Ignition's documented Basic architecture - a single gateway
|
|
4
|
+
that does everything - surfaced as a named architecture so the wizard can
|
|
5
|
+
offer it alongside the multi-gateway architectures. The only knobs are the
|
|
6
|
+
database choice (defaults to Postgres) and the optional reverse-proxy
|
|
7
|
+
scaffold.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
from dataclasses import dataclass
|
|
13
|
+
|
|
14
|
+
from ignition_stack.architectures.base import Architecture, ArchOptions, register
|
|
15
|
+
from ignition_stack.config import DatabaseConfig, GatewayConfig, ProjectConfig
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class BasicArchitecture:
|
|
20
|
+
slug: str = "basic"
|
|
21
|
+
summary: str = "One full Ignition 8.3 gateway + Postgres. The default starter stack."
|
|
22
|
+
|
|
23
|
+
def build(self, name: str, options: ArchOptions) -> ProjectConfig:
|
|
24
|
+
gateway = GatewayConfig()
|
|
25
|
+
if options.edge_role in {"gateway", "basic"}:
|
|
26
|
+
gateway = gateway.model_copy(update={"ignition_edition": "edge"})
|
|
27
|
+
|
|
28
|
+
return ProjectConfig(
|
|
29
|
+
name=name,
|
|
30
|
+
architecture=self.slug,
|
|
31
|
+
gateways=[gateway],
|
|
32
|
+
database=_database(options),
|
|
33
|
+
services=list(options.services),
|
|
34
|
+
reverse_proxy=options.reverse_proxy,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _database(options: ArchOptions) -> DatabaseConfig | None:
|
|
39
|
+
if options.database_kind is None:
|
|
40
|
+
return None
|
|
41
|
+
return DatabaseConfig(kind=options.database_kind)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# Side-effect: registers this architecture when the module is imported.
|
|
45
|
+
architecture: Architecture = register(BasicArchitecture())
|
|
@@ -1,25 +1,25 @@
|
|
|
1
|
-
"""Carry a resolved service registry across a ``switch-
|
|
1
|
+
"""Carry a resolved service registry across a ``switch-arch`` reshape.
|
|
2
2
|
|
|
3
|
-
``switch-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
``switch-arch`` rebuilds a stack under a new architecture. The architecture
|
|
4
|
+
only knows how to lay down topology + the inputs ``ArchOptions`` carries; it
|
|
5
|
+
cannot know about the *richer* registry shapes a hand-authored or
|
|
6
|
+
composer-built config can hold:
|
|
7
7
|
|
|
8
8
|
- a custom instance ``id`` that differs from its service slug;
|
|
9
9
|
- per-instance ``image`` / ``env`` / ``user`` / ``password`` overrides;
|
|
10
|
-
- a second database (distinct kind) beyond the
|
|
10
|
+
- a second database (distinct kind) beyond the architecture's default;
|
|
11
11
|
- partial / role-specific attachment sets (an instance attached to only some
|
|
12
12
|
gateways, not fanned to all).
|
|
13
13
|
|
|
14
|
-
Anything *not* expressible through ``
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
14
|
+
Anything *not* expressible through ``ArchOptions.services`` / ``database_kind``
|
|
15
|
+
rides here instead: :func:`carry_registry` re-grafts those instances onto the
|
|
16
|
+
freshly built config and re-maps their attachments by role into the new
|
|
17
|
+
topology. Attachments whose source gateway has no counterpart in the target
|
|
18
|
+
topology - or that would violate a new-topology invariant - are **dropped with
|
|
19
|
+
a printed advisory** rather than failing the reshape.
|
|
20
20
|
|
|
21
|
-
The IIoT overlay is *not* carried here: ``switch-
|
|
22
|
-
into ``
|
|
21
|
+
The IIoT overlay is *not* carried here: ``switch-arch`` recovers IIoT intent
|
|
22
|
+
into ``ArchOptions.iiot`` / ``iiot_broker`` so ``apply_iiot`` re-wires the
|
|
23
23
|
broker + Transmission/Engine roles natively in the new topology. The broker
|
|
24
24
|
instance and its ``mqtt-transmission`` / ``mqtt-engine`` attachments are
|
|
25
25
|
therefore skipped here to avoid double-wiring.
|
|
@@ -40,9 +40,9 @@ from ignition_stack.services.loader import load_all_services
|
|
|
40
40
|
from ignition_stack.services.manifest import ServiceManifest
|
|
41
41
|
|
|
42
42
|
# Roles that aggregate centrally (full gateways) versus roles that sit on the
|
|
43
|
-
# edge side and publish/scale out. A role-less gateway (
|
|
44
|
-
# is
|
|
45
|
-
#
|
|
43
|
+
# edge side and publish/scale out. A role-less gateway (basic) is central - it
|
|
44
|
+
# is the one full gateway in its topology. The two-way name preference
|
|
45
|
+
# (hub<->backend, spoke<->frontend) is tried before the class match.
|
|
46
46
|
_CENTRAL_ROLES = frozenset({"hub", "backend"})
|
|
47
47
|
_EDGE_SIDE_ROLES = frozenset({"spoke", "frontend"})
|
|
48
48
|
_ROLE_COUNTERPART = {
|
|
@@ -52,7 +52,7 @@ _ROLE_COUNTERPART = {
|
|
|
52
52
|
"frontend": "spoke",
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
# Attachment roles the IIoT overlay owns; carried via
|
|
55
|
+
# Attachment roles the IIoT overlay owns; carried via ArchOptions.iiot, never
|
|
56
56
|
# re-grafted here (apply_iiot re-wires them in the new topology).
|
|
57
57
|
_IIOT_ROLES = frozenset({"mqtt-transmission", "mqtt-engine"})
|
|
58
58
|
|
|
@@ -66,13 +66,13 @@ def is_default_representable(
|
|
|
66
66
|
config: ProjectConfig,
|
|
67
67
|
catalog: dict[str, ServiceManifest],
|
|
68
68
|
) -> bool:
|
|
69
|
-
"""True when ``instance`` is faithfully carried by ``
|
|
69
|
+
"""True when ``instance`` is faithfully carried by ``ArchOptions`` alone.
|
|
70
70
|
|
|
71
71
|
The "today's behavior" carry: an instance whose ``id`` equals its service
|
|
72
72
|
slug, carries no per-instance override, and is attached as a plain
|
|
73
73
|
``consumer`` on every *eligible* gateway (every gateway, minus the Edge
|
|
74
74
|
gateways a ``never_on_edge`` service skips). Such an instance is fully
|
|
75
|
-
reproduced by listing its slug in ``
|
|
75
|
+
reproduced by listing its slug in ``ArchOptions.services`` and letting
|
|
76
76
|
the resolver re-lower it, so it does not need re-grafting.
|
|
77
77
|
|
|
78
78
|
Databases are never representable through ``services`` (they ride
|
|
@@ -103,7 +103,7 @@ def is_default_representable(
|
|
|
103
103
|
|
|
104
104
|
|
|
105
105
|
def database_carried_by_kind(config: ProjectConfig, catalog: dict[str, ServiceManifest]) -> ServiceInstance | None:
|
|
106
|
-
"""The primary database iff it rides ``
|
|
106
|
+
"""The primary database iff it rides ``ArchOptions.database_kind`` cleanly.
|
|
107
107
|
|
|
108
108
|
``database_kind`` always rebuilds a database with the canonical id ``db``,
|
|
109
109
|
default credentials, the kind's default image, and a consumer attachment on
|
|
@@ -149,8 +149,8 @@ def detect_iiot_broker(config: ProjectConfig) -> str | None:
|
|
|
149
149
|
Detection is by attachment role: a stack carrying any ``mqtt-transmission``
|
|
150
150
|
or ``mqtt-engine`` attachment was built with ``apply_iiot``; the instance
|
|
151
151
|
those attachments reference is the broker. Returning its *service slug*
|
|
152
|
-
(not its id) lets ``switch-
|
|
153
|
-
``
|
|
152
|
+
(not its id) lets ``switch-arch`` re-apply the overlay with
|
|
153
|
+
``ArchOptions.iiot_broker``.
|
|
154
154
|
"""
|
|
155
155
|
by_id = {inst.id: inst for inst in config.service_instances}
|
|
156
156
|
for gw in config.gateways:
|
|
@@ -200,7 +200,7 @@ def carry_registry(
|
|
|
200
200
|
"""Re-graft ``old_config``'s richer registry onto the freshly built config.
|
|
201
201
|
|
|
202
202
|
Mutates and returns ``new_config``. For every old instance that is *not*
|
|
203
|
-
already default-representable (carried via ``
|
|
203
|
+
already default-representable (carried via ``ArchOptions``) and *not* the
|
|
204
204
|
IIoT broker, the instance is added to the registry (preserving id +
|
|
205
205
|
overrides) and its non-IIoT attachments are re-mapped by role into the new
|
|
206
206
|
topology. Attachments are dropped - each with a printed
|
|
@@ -212,7 +212,7 @@ def carry_registry(
|
|
|
212
212
|
|
|
213
213
|
The order matters: instances are added before attachments so an attachment's
|
|
214
214
|
referenced instance always exists, and the per-gateway db cap is checked
|
|
215
|
-
against attachments already present (including those the
|
|
215
|
+
against attachments already present (including those the architecture/lowering
|
|
216
216
|
produced) so the carry never over-attaches.
|
|
217
217
|
"""
|
|
218
218
|
catalog = load_all_services()
|
|
@@ -222,7 +222,7 @@ def carry_registry(
|
|
|
222
222
|
dropped_instance: dict[str, str] = {}
|
|
223
223
|
|
|
224
224
|
for old_inst in old_config.service_instances:
|
|
225
|
-
# The IIoT broker rides
|
|
225
|
+
# The IIoT broker rides ArchOptions.iiot; apply_iiot re-adds it.
|
|
226
226
|
if iiot_broker_slug is not None and old_inst.service == iiot_broker_slug:
|
|
227
227
|
continue
|
|
228
228
|
if is_default_representable(old_inst, old_config, catalog):
|
|
@@ -303,7 +303,7 @@ def _try_attach(
|
|
|
303
303
|
"""
|
|
304
304
|
manifest = catalog[inst.service]
|
|
305
305
|
if any(a.instance == inst.id and a.role == att.role for a in target.services):
|
|
306
|
-
return # already attached (idempotent /
|
|
306
|
+
return # already attached (idempotent / architecture already produced it)
|
|
307
307
|
|
|
308
308
|
if manifest.placement.never_on_edge and target.ignition_edition == "edge":
|
|
309
309
|
dropped.setdefault(inst.id, []).append(f"'{inst.service}' must not run on Edge gateway '{target.name}'")
|