ignition-stack 0.4.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.4.0 → ignition_stack-0.6.0}/.gitignore +3 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/PKG-INFO +7 -5
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/README.md +6 -4
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/builtin_modules.yaml +38 -0
- 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.4.0/ignition_stack/profiles → ignition_stack-0.6.0/ignition_stack/architectures}/advisory.py +1 -1
- ignition_stack-0.6.0/ignition_stack/architectures/base.py +344 -0
- ignition_stack-0.6.0/ignition_stack/architectures/basic.py +45 -0
- ignition_stack-0.6.0/ignition_stack/architectures/carry.py +319 -0
- {ignition_stack-0.4.0/ignition_stack/profiles → ignition_stack-0.6.0/ignition_stack/architectures}/hub_and_spoke.py +23 -23
- ignition_stack-0.4.0/ignition_stack/profiles/scaleout.py → ignition_stack-0.6.0/ignition_stack/architectures/scale_out.py +18 -17
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/catalog/builtins.py +47 -11
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/catalog/download.py +4 -14
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/catalog/schema.py +5 -18
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/cli.py +185 -109
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/commands/modules.py +1 -2
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/completion.py +37 -20
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/compose/engine.py +185 -46
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/compose/templates/footer.yaml.j2 +5 -1
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/compose/templates/services/ignition.yaml.j2 +15 -3
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/compose/writer.py +216 -53
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/config/__init__.py +4 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/config/io.py +3 -8
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/config/schema.py +342 -27
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/lifecycle/record.py +3 -3
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/lifecycle/regenerate.py +1 -1
- ignition_stack-0.6.0/ignition_stack/postsetup/generator.py +476 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/services/loader.py +1 -3
- ignition_stack-0.6.0/ignition_stack/services/manifest.py +263 -0
- ignition_stack-0.6.0/ignition_stack/services/resolver.py +388 -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 +8 -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 +17 -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 +21 -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 +16 -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 +4 -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 +17 -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 +24 -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 +19 -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 +4 -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 +17 -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 +4 -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 +16 -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 +17 -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 +18 -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 +36 -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 +19 -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 +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 +17 -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 +29 -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 +14 -0
- 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/identity-provider.md.j2 +25 -0
- ignition_stack-0.6.0/ignition_stack/templates/post-setup/mqtt-engine-connection.md.j2 +106 -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/compose.yaml.j2 +39 -0
- ignition_stack-0.6.0/ignition_stack/templates/services/chariot/manifest.yaml +48 -0
- ignition_stack-0.6.0/ignition_stack/templates/services/chariot/seed/service/chariot-trial.sh +56 -0
- ignition_stack-0.6.0/ignition_stack/templates/services/emqx/manifest.yaml +39 -0
- ignition_stack-0.6.0/ignition_stack/templates/services/hivemq/manifest.yaml +35 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/kafka/manifest.yaml +8 -1
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/keycloak/compose.yaml.j2 +2 -0
- ignition_stack-0.6.0/ignition_stack/templates/services/keycloak/manifest.yaml +41 -0
- ignition_stack-0.6.0/ignition_stack/templates/services/keycloak/seed/gateway-resources/config/resources/core/ignition/identity-provider/keycloak/config.json +57 -0
- ignition_stack-0.6.0/ignition_stack/templates/services/keycloak/seed/gateway-resources/config/resources/core/ignition/identity-provider/keycloak/resource.json +18 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/keycloak/seed/service/import/ignition-realm.json +23 -2
- ignition_stack-0.6.0/ignition_stack/templates/services/mariadb/manifest.yaml +28 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/modbus-sim/manifest.yaml +6 -1
- ignition_stack-0.6.0/ignition_stack/templates/services/mongo/manifest.yaml +27 -0
- ignition_stack-0.6.0/ignition_stack/templates/services/mysql/manifest.yaml +28 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/n8n/manifest.yaml +6 -1
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/opcua-sim/manifest.yaml +6 -1
- ignition_stack-0.6.0/ignition_stack/templates/services/postgres/manifest.yaml +39 -0
- ignition_stack-0.6.0/ignition_stack/templates/services/rabbitmq/manifest.yaml +41 -0
- ignition_stack-0.6.0/ignition_stack/wizard.py +1054 -0
- ignition_stack-0.6.0/ignition_stack/wizard_composer.py +716 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/modules.yaml +7 -7
- {ignition_stack-0.4.0/tests/golden/profiles → ignition_stack-0.6.0/tests/golden/architectures}/hub-and-spoke/docker-compose.yaml +0 -6
- ignition_stack-0.6.0/tests/golden/combos/hub-and-spoke-iiot-chariot/docker-compose.yaml +184 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/combos/network-split/docker-compose.yaml +2 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/scaleout-skeleton/docker-compose.yaml +0 -2
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/services/chariot/docker-compose.yaml +16 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/services/keycloak/docker-compose.yaml +2 -2
- ignition_stack-0.6.0/tests/test_architectures.py +1167 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/test_completion.py +8 -8
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/test_compose_engine.py +25 -23
- ignition_stack-0.6.0/tests/test_declarative_io.py +379 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/test_disable_builtins.py +80 -20
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/test_docs_cli_reference.py +6 -9
- ignition_stack-0.6.0/tests/test_iiot_overlay.py +384 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/test_init_standalone.py +9 -14
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/test_lifecycle.py +15 -17
- ignition_stack-0.6.0/tests/test_manifest_invariants.py +348 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/test_modules_catalog.py +1 -3
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/test_modules_cli.py +1 -3
- ignition_stack-0.6.0/tests/test_postsetup.py +279 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/test_redundancy.py +22 -33
- ignition_stack-0.6.0/tests/test_registry.py +291 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/test_service_catalog.py +117 -35
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/test_service_catalog_smoke.py +1 -3
- ignition_stack-0.6.0/tests/test_switch_arch_registry.py +345 -0
- 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.6.0/verification/iiot-spike/README.md +142 -0
- ignition_stack-0.4.0/ignition_stack/__init__.py +0 -1
- ignition_stack-0.4.0/ignition_stack/postsetup/generator.py +0 -245
- ignition_stack-0.4.0/ignition_stack/profiles/__init__.py +0 -31
- ignition_stack-0.4.0/ignition_stack/profiles/base.py +0 -236
- ignition_stack-0.4.0/ignition_stack/profiles/mcp_n8n.py +0 -55
- ignition_stack-0.4.0/ignition_stack/profiles/standalone.py +0 -44
- ignition_stack-0.4.0/ignition_stack/services/manifest.py +0 -106
- ignition_stack-0.4.0/ignition_stack/services/resolver.py +0 -186
- ignition_stack-0.4.0/ignition_stack/templates/post-setup/identity-provider.md.j2 +0 -13
- ignition_stack-0.4.0/ignition_stack/templates/post-setup/mqtt-engine-connection.md.j2 +0 -11
- ignition_stack-0.4.0/ignition_stack/templates/post-setup/reverse-proxy.md.j2 +0 -8
- ignition_stack-0.4.0/ignition_stack/templates/services/chariot/compose.yaml.j2 +0 -17
- ignition_stack-0.4.0/ignition_stack/templates/services/chariot/manifest.yaml +0 -22
- ignition_stack-0.4.0/ignition_stack/templates/services/emqx/manifest.yaml +0 -21
- ignition_stack-0.4.0/ignition_stack/templates/services/hivemq/manifest.yaml +0 -19
- ignition_stack-0.4.0/ignition_stack/templates/services/keycloak/manifest.yaml +0 -25
- ignition_stack-0.4.0/ignition_stack/templates/services/mariadb/manifest.yaml +0 -15
- ignition_stack-0.4.0/ignition_stack/templates/services/mongo/manifest.yaml +0 -14
- ignition_stack-0.4.0/ignition_stack/templates/services/mysql/manifest.yaml +0 -15
- ignition_stack-0.4.0/ignition_stack/templates/services/postgres/manifest.yaml +0 -21
- ignition_stack-0.4.0/ignition_stack/templates/services/rabbitmq/manifest.yaml +0 -23
- ignition_stack-0.4.0/ignition_stack/wizard.py +0 -472
- ignition_stack-0.4.0/tests/golden/profiles/mcp-n8n/docker-compose.yaml +0 -72
- ignition_stack-0.4.0/tests/test_declarative_io.py +0 -193
- ignition_stack-0.4.0/tests/test_postsetup.py +0 -84
- ignition_stack-0.4.0/tests/test_profiles.py +0 -844
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/LICENSE +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/catalog/__init__.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/catalog/loader.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/catalog/verify.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/commands/__init__.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/compose/__init__.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/compose/templates/header.yaml.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/compose/templates/services/bootstrap.yaml.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/lifecycle/__init__.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/lifecycle/cleanup.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/postsetup/__init__.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/services/__init__.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/__init__.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/_default.md.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/device-connection.md.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/gateway-network-link.md.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/kafka-connector.md.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/mcp-module.md.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/opc-ua-connection.md.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/post-setup/redundancy-pairing.md.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/redundancy/redundancy.xml.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/chariot/seed/service/USAGE.md +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/emqx/compose.yaml.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/emqx/seed/service/USAGE.md +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/hivemq/compose.yaml.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/hivemq/seed/service/USAGE.md +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/kafka/compose.yaml.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/kafka/seed/service/USAGE.md +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mariadb/compose.yaml.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mariadb/seed/service/initdb/00-create-extra-databases.sh +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/modbus-sim/compose.yaml.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/modbus-sim/seed/service/USAGE.md +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mongo/compose.yaml.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mongo/seed/service/initdb/01-demo-collection.js +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mysql/compose.yaml.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/mysql/seed/service/initdb/00-create-extra-databases.sh +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/n8n/compose.yaml.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/n8n/seed/service/USAGE.md +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/opcua-sim/compose.yaml.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/opcua-sim/seed/service/USAGE.md +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/compose.yaml.j2 +0 -0
- {ignition_stack-0.4.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.4.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.4.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.4.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.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/postgres/seed/service/initdb/00-create-extra-databases.sh +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/rabbitmq/compose.yaml.j2 +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/services/rabbitmq/seed/service/enabled_plugins +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/scripts/docker-bootstrap.sh +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/services/ignition/config/resources/core/config-mode.json +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/services/ignition/config/resources/dev/config-mode.json +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/templates/standalone-postgres/services/ignition/projects/.gitkeep +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/ignition_stack/update_check.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/pyproject.toml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/__init__.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/conftest.py +0 -0
- {ignition_stack-0.4.0/tests/golden/profiles/standalone → ignition_stack-0.6.0/tests/golden/architectures/basic}/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0/tests/golden/profiles/scaleout → ignition_stack-0.6.0/tests/golden/architectures/scale-out}/docker-compose.yaml +0 -0
- {ignition_stack-0.4.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.4.0 → ignition_stack-0.6.0}/tests/golden/combos/smoke-stack/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/services/db-mariadb/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/services/db-mongo/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/services/db-mysql/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/services/db-postgres/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/services/emqx/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/services/hivemq/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/services/kafka/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/services/modbus-sim/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/services/n8n/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/services/opcua-sim/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/services/rabbitmq/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/golden/standalone-postgres/docker-compose.yaml +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/test_builtin_catalog_smoke.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/tests/test_update_check.py +0 -0
- {ignition_stack-0.4.0 → ignition_stack-0.6.0}/verification/redundancy-spike/README.md +0 -0
- {ignition_stack-0.4.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.
|
|
@@ -25,6 +25,15 @@
|
|
|
25
25
|
# `identifier` is the fully-qualified module id used verbatim in
|
|
26
26
|
# GATEWAY_MODULES_ENABLED. `slug` is the friendly kebab name a user puts in
|
|
27
27
|
# `disable_builtins`. `name` is the gateway's display name (for wizard labels).
|
|
28
|
+
#
|
|
29
|
+
# `default_enabled` marks the curated "typical demo" set the interactive wizard
|
|
30
|
+
# pre-checks (opt-in selection). It does NOT change the engine math or the
|
|
31
|
+
# non-interactive profile path - profiles still enable every built-in. It only
|
|
32
|
+
# seeds the wizard's checkbox so the common path is a lean gateway. The JDBC
|
|
33
|
+
# drivers are all `default_enabled: false` on purpose: the wizard enables the
|
|
34
|
+
# one matching the chosen database (see `jdbc_driver_for`), not a static one.
|
|
35
|
+
# The smoke guard checks the module *set*, not this curation flag, so the
|
|
36
|
+
# default-set invariants live in tests/test_disable_builtins.py instead.
|
|
28
37
|
|
|
29
38
|
version: 1
|
|
30
39
|
|
|
@@ -36,87 +45,116 @@ modules:
|
|
|
36
45
|
- slug: alarm-notification
|
|
37
46
|
identifier: com.inductiveautomation.alarm-notification
|
|
38
47
|
name: Alarm Notification
|
|
48
|
+
default_enabled: true
|
|
39
49
|
- slug: allen-bradley-driver
|
|
40
50
|
identifier: com.inductiveautomation.opcua.drivers.ablegacy
|
|
41
51
|
name: Allen-Bradley Driver
|
|
52
|
+
default_enabled: false
|
|
42
53
|
- slug: bacnet-driver
|
|
43
54
|
identifier: com.inductiveautomation.opcua.drivers.bacnet
|
|
44
55
|
name: BACnet Driver
|
|
56
|
+
default_enabled: false
|
|
45
57
|
- slug: enterprise-administration
|
|
46
58
|
identifier: com.inductiveautomation.eam
|
|
47
59
|
name: Enterprise Administration
|
|
60
|
+
default_enabled: false
|
|
48
61
|
- slug: event-streams
|
|
49
62
|
identifier: com.inductiveautomation.eventstream
|
|
50
63
|
name: Event Streams
|
|
64
|
+
default_enabled: false
|
|
51
65
|
- slug: historian-core
|
|
52
66
|
identifier: com.inductiveautomation.historian
|
|
53
67
|
name: Historian Core
|
|
68
|
+
default_enabled: true
|
|
54
69
|
- slug: kafka-connector
|
|
55
70
|
identifier: com.inductiveautomation.connectors.kafka
|
|
56
71
|
name: Kafka Connector
|
|
72
|
+
default_enabled: false
|
|
57
73
|
- slug: legacy-dnp3-driver
|
|
58
74
|
identifier: com.inductiveautomation.opcua.drivers.dnp3
|
|
59
75
|
name: Legacy DNP3 Driver
|
|
76
|
+
default_enabled: false
|
|
60
77
|
- slug: logix-driver
|
|
61
78
|
identifier: com.inductiveautomation.opcua.drivers.logix
|
|
62
79
|
name: Logix Driver
|
|
80
|
+
default_enabled: false
|
|
63
81
|
- slug: mariadb-jdbc-driver
|
|
64
82
|
identifier: com.inductiveautomation.jdbc.mariadb
|
|
65
83
|
name: MariaDB JDBC Driver
|
|
84
|
+
default_enabled: false
|
|
66
85
|
- slug: micro800-driver
|
|
67
86
|
identifier: com.inductiveautomation.opcua.drivers.micro800
|
|
68
87
|
name: Micro800 Driver
|
|
88
|
+
default_enabled: false
|
|
69
89
|
- slug: mitsubishi-driver
|
|
70
90
|
identifier: com.inductiveautomation.opcua.drivers.mitsubishi
|
|
71
91
|
name: Mitsubishi Driver
|
|
92
|
+
default_enabled: false
|
|
72
93
|
- slug: modbus-driver
|
|
73
94
|
identifier: com.inductiveautomation.opcua.drivers.modbus
|
|
74
95
|
name: Modbus Driver
|
|
96
|
+
default_enabled: false
|
|
75
97
|
- slug: mssql-jdbc-driver
|
|
76
98
|
identifier: com.inductiveautomation.jdbc.mssql
|
|
77
99
|
name: MSSQL JDBC Driver
|
|
100
|
+
default_enabled: false
|
|
78
101
|
- slug: omron-driver
|
|
79
102
|
identifier: com.inductiveautomation.opcua.drivers.omron
|
|
80
103
|
name: Omron Driver
|
|
104
|
+
default_enabled: false
|
|
81
105
|
- slug: opc-ua
|
|
82
106
|
identifier: com.inductiveautomation.opcua
|
|
83
107
|
name: OPC-UA
|
|
108
|
+
default_enabled: true
|
|
84
109
|
- slug: perspective
|
|
85
110
|
identifier: com.inductiveautomation.perspective
|
|
86
111
|
name: Perspective
|
|
112
|
+
default_enabled: true
|
|
87
113
|
- slug: postgresql-jdbc-driver
|
|
88
114
|
identifier: com.inductiveautomation.jdbc.postgresql
|
|
89
115
|
name: PostgreSQL JDBC Driver
|
|
116
|
+
default_enabled: false
|
|
90
117
|
- slug: reporting
|
|
91
118
|
identifier: com.inductiveautomation.reporting
|
|
92
119
|
name: Reporting
|
|
120
|
+
default_enabled: true
|
|
93
121
|
- slug: sfc
|
|
94
122
|
identifier: com.inductiveautomation.sfc
|
|
95
123
|
name: SFC
|
|
124
|
+
default_enabled: false
|
|
96
125
|
- slug: siemens-drivers
|
|
97
126
|
identifier: com.inductiveautomation.opcua.drivers.siemens
|
|
98
127
|
name: Siemens Drivers
|
|
128
|
+
default_enabled: false
|
|
99
129
|
- slug: siemens-enhanced-driver
|
|
100
130
|
identifier: com.inductiveautomation.opcua.drivers.siemens-symbolic
|
|
101
131
|
name: Siemens Enhanced Driver
|
|
132
|
+
default_enabled: false
|
|
102
133
|
- slug: sms-notification
|
|
103
134
|
identifier: com.inductiveautomation.sms-notification
|
|
104
135
|
name: SMS Notification
|
|
136
|
+
default_enabled: false
|
|
105
137
|
- slug: sql-bridge
|
|
106
138
|
identifier: com.inductiveautomation.sqlbridge
|
|
107
139
|
name: SQL Bridge
|
|
140
|
+
default_enabled: true
|
|
108
141
|
- slug: sql-historian
|
|
109
142
|
identifier: com.inductiveautomation.historian.sql
|
|
110
143
|
name: SQL Historian
|
|
144
|
+
default_enabled: true
|
|
111
145
|
- slug: symbol-factory
|
|
112
146
|
identifier: com.inductiveautomation.symbol-factory
|
|
113
147
|
name: Symbol Factory
|
|
148
|
+
default_enabled: false
|
|
114
149
|
- slug: udp-and-tcp-drivers
|
|
115
150
|
identifier: com.inductiveautomation.opcua.drivers.tcpudp
|
|
116
151
|
name: UDP and TCP Drivers
|
|
152
|
+
default_enabled: false
|
|
117
153
|
- slug: vision
|
|
118
154
|
identifier: com.inductiveautomation.vision
|
|
119
155
|
name: Vision
|
|
156
|
+
default_enabled: false
|
|
120
157
|
- slug: webdev
|
|
121
158
|
identifier: com.inductiveautomation.webdev
|
|
122
159
|
name: WebDev
|
|
160
|
+
default_enabled: false
|
|
@@ -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:
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"""Architecture contract + options + registry.
|
|
2
|
+
|
|
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.
|
|
9
|
+
|
|
10
|
+
Two-stage pipeline:
|
|
11
|
+
|
|
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
|
|
16
|
+
``services.resolver.resolve()`` then expands the usual implicit deps on.
|
|
17
|
+
|
|
18
|
+
Each architecture is a small dataclass with three pieces:
|
|
19
|
+
|
|
20
|
+
- ``slug`` - the wizard/flag value users type.
|
|
21
|
+
- ``summary`` - one-line description for the wizard menu + docs.
|
|
22
|
+
- ``build`` - pure function ``(name, options) -> ProjectConfig``.
|
|
23
|
+
|
|
24
|
+
Keeping ``build`` pure (no I/O, no prompts) is what lets the wizard layer
|
|
25
|
+
and the CLI flag layer share the same code path and stay testable.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
from dataclasses import dataclass
|
|
31
|
+
from typing import Protocol
|
|
32
|
+
|
|
33
|
+
from ignition_stack.config import (
|
|
34
|
+
GatewayConfig,
|
|
35
|
+
ProjectConfig,
|
|
36
|
+
RedundancyConfig,
|
|
37
|
+
ReverseProxyConfig,
|
|
38
|
+
ServiceAttachment,
|
|
39
|
+
ServiceInstance,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class ArchOptions:
|
|
45
|
+
"""Inputs each architecture reads to shape the resolved config.
|
|
46
|
+
|
|
47
|
+
Every field has a sensible default so callers only set what they
|
|
48
|
+
actually care about. The wizard fills in many of these from prompts;
|
|
49
|
+
the non-interactive CLI path fills in a subset from flags and leaves
|
|
50
|
+
the rest at their defaults.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
spokes: int = 3
|
|
54
|
+
"""Hub-and-spoke spoke count. Ignored by other architectures."""
|
|
55
|
+
|
|
56
|
+
frontends: int = 1
|
|
57
|
+
"""Scale-out frontend gateway count. Ignored by other architectures.
|
|
58
|
+
|
|
59
|
+
1 yields a single gateway named ``frontend``; N>1 yields
|
|
60
|
+
``frontend-1``..``frontend-N``. A ``backend`` gateway is always added
|
|
61
|
+
on top.
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
force: bool = False
|
|
65
|
+
"""Bypass the hub-and-spoke red-tier advisory. Ignored elsewhere."""
|
|
66
|
+
|
|
67
|
+
network_split: bool | None = None
|
|
68
|
+
"""Tri-state override for the frontend/backend network split.
|
|
69
|
+
|
|
70
|
+
``None`` lets each architecture apply its own default (scale-out splits,
|
|
71
|
+
hub-and-spoke does not). ``True``/``False`` force the split on or off
|
|
72
|
+
regardless of the architecture default.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
edge_role: str | None = None
|
|
76
|
+
"""Which gateway role (if any) runs the Edge edition.
|
|
77
|
+
|
|
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
|
|
80
|
+
apply its own default when this is None.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
reverse_proxy: ReverseProxyConfig | None = None
|
|
84
|
+
"""Reverse-proxy scaffolding. None = plain host-port mapping."""
|
|
85
|
+
|
|
86
|
+
database_kind: str | None = "postgres"
|
|
87
|
+
"""SQL database for the stack. None = no database (gateway-only)."""
|
|
88
|
+
|
|
89
|
+
services: tuple[str, ...] = ()
|
|
90
|
+
"""Additional service catalog slugs the user picked beyond architecture defaults."""
|
|
91
|
+
|
|
92
|
+
redundant_role: str | None = None
|
|
93
|
+
"""Role (or gateway name) to make redundant, expanding it into a master +
|
|
94
|
+
backup pair. ``None`` (default) builds no redundancy. Must name a singleton
|
|
95
|
+
workhorse role (scale-out 'backend', hub-and-spoke 'hub', basic
|
|
96
|
+
'gateway'); replicated tiers ('frontend', 'spoke') are rejected."""
|
|
97
|
+
|
|
98
|
+
disable_builtins: tuple[str, ...] = ()
|
|
99
|
+
"""Built-in module slugs to turn off on every gateway in the stack.
|
|
100
|
+
|
|
101
|
+
Empty (default) leaves all built-ins on. Applied uniformly by
|
|
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``."""
|
|
105
|
+
|
|
106
|
+
iiot: bool = False
|
|
107
|
+
"""Overlay an MQTT/Sparkplug IIoT pipeline (a broker + Cirrus Link
|
|
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``."""
|
|
111
|
+
|
|
112
|
+
iiot_broker: str | None = None
|
|
113
|
+
"""MQTT broker slug the IIoT overlay wires to. ``None`` with ``iiot`` on
|
|
114
|
+
means the confirmed default ``chariot`` (Cirrus Link's own broker). Ignored
|
|
115
|
+
when ``iiot`` is False. Validated against the catalog by :func:`apply_iiot`."""
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class Architecture(Protocol):
|
|
119
|
+
"""A factory that turns ``ArchOptions`` into a ``ProjectConfig``."""
|
|
120
|
+
|
|
121
|
+
slug: str
|
|
122
|
+
summary: str
|
|
123
|
+
|
|
124
|
+
def build(self, name: str, options: ArchOptions) -> ProjectConfig: ...
|
|
125
|
+
|
|
126
|
+
|
|
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] = {}
|
|
130
|
+
|
|
131
|
+
|
|
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())``.
|
|
135
|
+
"""
|
|
136
|
+
if architecture.slug in _REGISTRY:
|
|
137
|
+
raise ValueError(f"architecture '{architecture.slug}' is already registered")
|
|
138
|
+
_REGISTRY[architecture.slug] = architecture
|
|
139
|
+
return architecture
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def get_architecture(slug: str) -> Architecture:
|
|
143
|
+
"""Look up a registered architecture by slug. Raises ``KeyError`` if unknown."""
|
|
144
|
+
try:
|
|
145
|
+
return _REGISTRY[slug]
|
|
146
|
+
except KeyError as exc:
|
|
147
|
+
known = ", ".join(sorted(_REGISTRY))
|
|
148
|
+
raise KeyError(f"unknown architecture '{slug}'; known architectures: {known}") from exc
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def list_architectures() -> list[Architecture]:
|
|
152
|
+
"""All registered architectures in stable insertion order."""
|
|
153
|
+
return list(_REGISTRY.values())
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
# Roles that are horizontally replicated, not paired. Ignition redundancy is a
|
|
157
|
+
# two-node master/backup arrangement, so a tier that can have many members
|
|
158
|
+
# (frontends, spokes) is never the redundancy target - those scale out, they
|
|
159
|
+
# don't fail over. Marking one redundant is a usage error.
|
|
160
|
+
_NON_REDUNDANT_ROLES = frozenset({"frontend", "spoke"})
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _matching_gateways(config: ProjectConfig, redundant_role: str) -> list:
|
|
164
|
+
"""Gateways a redundant role names, matched by role or name.
|
|
165
|
+
|
|
166
|
+
The single matching rule shared by :func:`mark_redundant` (which errors on a
|
|
167
|
+
bad match) and :func:`can_host_redundant_role` (which only reports), so the
|
|
168
|
+
two can never drift on what "matches" means.
|
|
169
|
+
"""
|
|
170
|
+
return [gw for gw in config.gateways if redundant_role in (gw.role, gw.name)]
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def can_host_redundant_role(config: ProjectConfig, redundant_role: str) -> bool:
|
|
174
|
+
"""True when ``config`` has exactly one gateway that can be paired as master.
|
|
175
|
+
|
|
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
|
|
179
|
+
``gateway``) simply may not exist in the destination.
|
|
180
|
+
"""
|
|
181
|
+
if redundant_role in _NON_REDUNDANT_ROLES:
|
|
182
|
+
return False
|
|
183
|
+
return len(_matching_gateways(config, redundant_role)) == 1
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def mark_redundant(config: ProjectConfig, redundant_role: str | None) -> ProjectConfig:
|
|
187
|
+
"""Stamp the gateway named/roled ``redundant_role`` as a redundancy master.
|
|
188
|
+
|
|
189
|
+
Returns ``config`` unchanged when ``redundant_role`` is None. The expansion
|
|
190
|
+
into a master+backup pair happens later in
|
|
191
|
+
:func:`ignition_stack.services.resolver.resolve`; this only marks which
|
|
192
|
+
gateway to pair, so the same logic serves every architecture and the wizard.
|
|
193
|
+
|
|
194
|
+
Raises ``ValueError`` (surfaced by the CLI as a usage error, exit code 2)
|
|
195
|
+
when the role is a replicated frontend/spoke tier, unknown, or ambiguous
|
|
196
|
+
(matches more than one gateway - you can't pair a horizontally-scaled tier).
|
|
197
|
+
"""
|
|
198
|
+
if redundant_role is None:
|
|
199
|
+
return config
|
|
200
|
+
if redundant_role in _NON_REDUNDANT_ROLES:
|
|
201
|
+
raise ValueError(
|
|
202
|
+
f"role '{redundant_role}' is horizontally replicated, not paired; "
|
|
203
|
+
"redundancy applies to a single gateway (e.g. a scale-out 'backend' "
|
|
204
|
+
"or a hub-and-spoke 'hub'), never to frontends or spokes"
|
|
205
|
+
)
|
|
206
|
+
matches = _matching_gateways(config, redundant_role)
|
|
207
|
+
if not matches:
|
|
208
|
+
known = ", ".join(sorted({gw.role or gw.name for gw in config.gateways}))
|
|
209
|
+
raise ValueError(f"no gateway matches redundant role '{redundant_role}'; available roles: {known}")
|
|
210
|
+
if len(matches) > 1:
|
|
211
|
+
raise ValueError(f"redundant role '{redundant_role}' matches {len(matches)} gateways; " "redundancy pairs a single gateway, so name a singleton role")
|
|
212
|
+
master = matches[0]
|
|
213
|
+
master.redundancy = RedundancyConfig(mode="master", peer=f"{master.name}-backup")
|
|
214
|
+
return config
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def build_architecture(slug: str, name: str, options: ArchOptions) -> ProjectConfig:
|
|
218
|
+
"""Materialize a ``ProjectConfig`` for the named architecture.
|
|
219
|
+
|
|
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.
|
|
225
|
+
"""
|
|
226
|
+
config = get_architecture(slug).build(name, options)
|
|
227
|
+
config = mark_redundant(config, options.redundant_role)
|
|
228
|
+
config = apply_disable_builtins(config, options.disable_builtins)
|
|
229
|
+
broker = (options.iiot_broker or _IIOT_DEFAULT_BROKER) if options.iiot else None
|
|
230
|
+
return apply_iiot(config, broker)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def apply_disable_builtins(config: ProjectConfig, disable_builtins: tuple[str, ...]) -> ProjectConfig:
|
|
234
|
+
"""Stamp ``disable_builtins`` onto every gateway in ``config``.
|
|
235
|
+
|
|
236
|
+
Applied centrally (like :func:`mark_redundant`) so one rule serves every
|
|
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.
|
|
240
|
+
"""
|
|
241
|
+
if not disable_builtins:
|
|
242
|
+
return config
|
|
243
|
+
# pydantic does not re-validate on attribute assignment, so validate here -
|
|
244
|
+
# this is the wizard/CLI choke point (the declarative path is checked at
|
|
245
|
+
# construction). Raises ValueError on an unknown slug; the CLI maps that to
|
|
246
|
+
# exit code 2.
|
|
247
|
+
from ignition_stack.catalog.builtins import validate_disable_slugs
|
|
248
|
+
|
|
249
|
+
validate_disable_slugs(list(disable_builtins))
|
|
250
|
+
for gw in config.gateways:
|
|
251
|
+
gw.disable_builtins = list(disable_builtins)
|
|
252
|
+
return config
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
# The confirmed default IIoT broker (Cirrus Link's own Chariot, the most
|
|
256
|
+
# official pairing with their Transmission/Engine modules). Used when the
|
|
257
|
+
# overlay is requested without an explicit broker slug.
|
|
258
|
+
_IIOT_DEFAULT_BROKER = "chariot"
|
|
259
|
+
|
|
260
|
+
# Gateway roles that run MQTT Transmission (edge-side: publish Sparkplug to the
|
|
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.
|
|
264
|
+
_TRANSMISSION_ROLES = frozenset({"spoke", "frontend"})
|
|
265
|
+
_ENGINE_ROLES = frozenset({"hub", "backend"})
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def apply_iiot(config: ProjectConfig, broker: str | None) -> ProjectConfig:
|
|
269
|
+
"""Overlay an MQTT/Sparkplug IIoT pipeline onto ``config``.
|
|
270
|
+
|
|
271
|
+
Returns ``config`` unchanged when ``broker`` is None. Otherwise it adds the
|
|
272
|
+
broker as a stack-level :class:`ServiceInstance` (singleton, enforced by the
|
|
273
|
+
resolver) and wires each gateway by role, using the **module slugs the broker
|
|
274
|
+
manifest's ``wires.mqtt`` block names** (never hardcoded):
|
|
275
|
+
|
|
276
|
+
- ``spoke`` / ``frontend`` gateways get an ``mqtt-transmission`` attachment
|
|
277
|
+
plus the Transmission module (they publish Sparkplug to the broker);
|
|
278
|
+
- ``hub`` / ``backend`` gateways get an ``mqtt-engine`` attachment plus the
|
|
279
|
+
Engine module (they subscribe and aggregate);
|
|
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.
|
|
283
|
+
|
|
284
|
+
Brokers are not ``never_on_edge``, so an Edge spoke attaching with role
|
|
285
|
+
``mqtt-transmission`` is correct and expected. Idempotent: guards on
|
|
286
|
+
``(instance, role)`` attachment pairs and on module-already-present, so
|
|
287
|
+
``apply_iiot(apply_iiot(c)) == apply_iiot(c)``. The expansion of a redundancy
|
|
288
|
+
master happens later in :func:`~ignition_stack.services.resolver.resolve`,
|
|
289
|
+
which copies the master's attachments + modules onto the backup, so a
|
|
290
|
+
redundant Engine gateway ends up with the Engine module on both nodes.
|
|
291
|
+
|
|
292
|
+
Raises ``ValueError`` (surfaced by the CLI as exit code 2) when the broker
|
|
293
|
+
slug is unknown, is not an ``mqtt-broker``, or carries no ``wires.mqtt`` block.
|
|
294
|
+
"""
|
|
295
|
+
if broker is None:
|
|
296
|
+
return config
|
|
297
|
+
|
|
298
|
+
from ignition_stack.services.loader import load_all_services
|
|
299
|
+
|
|
300
|
+
catalog = load_all_services()
|
|
301
|
+
manifest = catalog.get(broker)
|
|
302
|
+
if manifest is None:
|
|
303
|
+
brokers = ", ".join(sorted(slug for slug, m in catalog.items() if m.kind == "mqtt-broker"))
|
|
304
|
+
raise ValueError(f"unknown iiot broker '{broker}'; known mqtt brokers: {brokers}")
|
|
305
|
+
if manifest.kind != "mqtt-broker":
|
|
306
|
+
raise ValueError(f"iiot broker '{broker}' is a '{manifest.kind}' service, not an mqtt-broker")
|
|
307
|
+
if manifest.wires is None or manifest.wires.mqtt is None:
|
|
308
|
+
raise ValueError(f"mqtt broker '{broker}' declares no wires.mqtt block, so the IIoT overlay cannot find its Transmission/Engine module slugs")
|
|
309
|
+
mqtt = manifest.wires.mqtt
|
|
310
|
+
|
|
311
|
+
# Add the broker instance once (id == slug); the resolver's singleton check
|
|
312
|
+
# catches an accidental duplicate broker elsewhere in the registry.
|
|
313
|
+
if not any(inst.id == broker for inst in config.service_instances):
|
|
314
|
+
config.service_instances.append(ServiceInstance(id=broker, service=broker))
|
|
315
|
+
|
|
316
|
+
transmission_gws = [gw for gw in config.gateways if gw.role in _TRANSMISSION_ROLES]
|
|
317
|
+
engine_gws = [gw for gw in config.gateways if gw.role in _ENGINE_ROLES]
|
|
318
|
+
|
|
319
|
+
if not transmission_gws and not engine_gws:
|
|
320
|
+
# No transmission/engine roles in this topology: run the whole loop on
|
|
321
|
+
# the single/first gateway so the demo is self-contained.
|
|
322
|
+
only = config.gateways[0]
|
|
323
|
+
_wire_iiot_gateway(only, broker, "mqtt-transmission", mqtt.transmission_module)
|
|
324
|
+
_wire_iiot_gateway(only, broker, "mqtt-engine", mqtt.engine_module)
|
|
325
|
+
else:
|
|
326
|
+
for gw in transmission_gws:
|
|
327
|
+
_wire_iiot_gateway(gw, broker, "mqtt-transmission", mqtt.transmission_module)
|
|
328
|
+
for gw in engine_gws:
|
|
329
|
+
_wire_iiot_gateway(gw, broker, "mqtt-engine", mqtt.engine_module)
|
|
330
|
+
return config
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _wire_iiot_gateway(gw: GatewayConfig, instance_id: str, role: str, module: str) -> None:
|
|
334
|
+
"""Attach ``gw`` to the broker with ``role`` and add ``module`` to it.
|
|
335
|
+
|
|
336
|
+
Role-aware guard (not the phase-1 instance-only ``_attach_all_gateways``): a
|
|
337
|
+
self-loop gateway holds two attachments to the same broker instance, one per
|
|
338
|
+
role, so the guard must key on the ``(instance, role)`` pair. Module presence
|
|
339
|
+
is guarded separately so a second pass adds nothing.
|
|
340
|
+
"""
|
|
341
|
+
if not any(att.instance == instance_id and att.role == role for att in gw.services):
|
|
342
|
+
gw.services.append(ServiceAttachment(instance=instance_id, role=role))
|
|
343
|
+
if module not in gw.modules:
|
|
344
|
+
gw.modules.append(module)
|
|
@@ -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())
|