ignition-stack 0.1.1__tar.gz → 0.3.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.1.1 → ignition_stack-0.3.0}/.gitignore +0 -5
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/PKG-INFO +1 -4
- ignition_stack-0.3.0/builtin_modules.yaml +122 -0
- ignition_stack-0.3.0/ignition_stack/__init__.py +1 -0
- ignition_stack-0.3.0/ignition_stack/catalog/builtins.py +171 -0
- ignition_stack-0.3.0/ignition_stack/cli.py +563 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/completion.py +48 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/compose/engine.py +44 -8
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/compose/templates/services/ignition.yaml.j2 +17 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/compose/writer.py +51 -8
- ignition_stack-0.3.0/ignition_stack/config/__init__.py +20 -0
- ignition_stack-0.3.0/ignition_stack/config/io.py +125 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/config/schema.py +113 -2
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/lifecycle/__init__.py +1 -1
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/lifecycle/cleanup.py +3 -2
- ignition_stack-0.3.0/ignition_stack/lifecycle/record.py +73 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/lifecycle/regenerate.py +2 -2
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/postsetup/generator.py +38 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/profiles/__init__.py +4 -0
- ignition_stack-0.3.0/ignition_stack/profiles/base.py +236 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/profiles/hub_and_spoke.py +5 -0
- ignition_stack-0.3.0/ignition_stack/profiles/scaleout.py +80 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/services/resolver.py +54 -1
- ignition_stack-0.3.0/ignition_stack/templates/post-setup/redundancy-pairing.md.j2 +24 -0
- ignition_stack-0.3.0/ignition_stack/templates/redundancy/redundancy.xml.j2 +25 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/standalone-postgres/scripts/docker-bootstrap.sh +9 -1
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/wizard.py +117 -7
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/modules.yaml +1 -1
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/pyproject.toml +2 -6
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/profiles/scaleout/docker-compose.yaml +0 -1
- ignition_stack-0.3.0/tests/golden/profiles/scaleout-redundant/docker-compose.yaml +155 -0
- ignition_stack-0.3.0/tests/test_builtin_catalog_smoke.py +114 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/test_compose_engine.py +3 -4
- ignition_stack-0.3.0/tests/test_declarative_io.py +193 -0
- ignition_stack-0.3.0/tests/test_disable_builtins.py +249 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/test_docs_cli_reference.py +3 -1
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/test_lifecycle.py +51 -43
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/test_profiles.py +189 -3
- ignition_stack-0.3.0/tests/test_redundancy.py +267 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/test_service_catalog.py +16 -8
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/test_service_catalog_smoke.py +3 -3
- ignition_stack-0.3.0/verification/redundancy-spike/README.md +239 -0
- ignition_stack-0.3.0/verification/smoke/README.md +58 -0
- ignition_stack-0.1.1/ignition_stack/__init__.py +0 -1
- ignition_stack-0.1.1/ignition_stack/cli.py +0 -354
- ignition_stack-0.1.1/ignition_stack/config/__init__.py +0 -8
- ignition_stack-0.1.1/ignition_stack/lifecycle/record.py +0 -67
- ignition_stack-0.1.1/ignition_stack/profiles/base.py +0 -108
- ignition_stack-0.1.1/ignition_stack/profiles/scaleout.py +0 -65
- ignition_stack-0.1.1/scripts/seeding-poc/README.md +0 -37
- ignition_stack-0.1.1/verification/phase-1-3/README.md +0 -58
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/LICENSE +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/README.md +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/catalog/__init__.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/catalog/download.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/catalog/loader.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/catalog/schema.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/catalog/verify.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/commands/__init__.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/commands/modules.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/compose/__init__.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/compose/templates/footer.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/compose/templates/header.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/compose/templates/services/bootstrap.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/postsetup/__init__.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/profiles/advisory.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/profiles/mcp_n8n.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/profiles/standalone.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/services/__init__.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/services/loader.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/services/manifest.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/__init__.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/post-setup/_default.md.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/post-setup/device-connection.md.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/post-setup/gateway-network-link.md.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/post-setup/identity-provider.md.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/post-setup/kafka-connector.md.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/post-setup/mcp-module.md.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/post-setup/mqtt-engine-connection.md.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/post-setup/opc-ua-connection.md.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/post-setup/reverse-proxy.md.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/chariot/compose.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/chariot/manifest.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/chariot/seed/service/USAGE.md +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/emqx/compose.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/emqx/manifest.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/emqx/seed/service/USAGE.md +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/hivemq/compose.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/hivemq/manifest.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/hivemq/seed/service/USAGE.md +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/kafka/compose.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/kafka/manifest.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/kafka/seed/service/USAGE.md +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/keycloak/compose.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/keycloak/manifest.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/keycloak/seed/service/import/ignition-realm.json +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/mariadb/compose.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/mariadb/manifest.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/mariadb/seed/service/initdb/00-create-extra-databases.sh +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/modbus-sim/compose.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/modbus-sim/manifest.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/modbus-sim/seed/service/USAGE.md +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/mongo/compose.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/mongo/manifest.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/mongo/seed/service/initdb/01-demo-collection.js +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/mysql/compose.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/mysql/manifest.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/mysql/seed/service/initdb/00-create-extra-databases.sh +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/n8n/compose.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/n8n/manifest.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/n8n/seed/service/USAGE.md +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/opcua-sim/compose.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/opcua-sim/manifest.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/opcua-sim/seed/service/USAGE.md +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/postgres/compose.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/postgres/manifest.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/database-connection/db/config.json +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/postgres/seed/gateway-resources/config/resources/core/ignition/database-connection/db/resource.json +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.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.1.1 → ignition_stack-0.3.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.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/postgres/seed/service/initdb/00-create-extra-databases.sh +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/rabbitmq/compose.yaml.j2 +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/rabbitmq/manifest.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/services/rabbitmq/seed/service/enabled_plugins +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/standalone-postgres/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/standalone-postgres/services/ignition/config/resources/core/config-mode.json +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/standalone-postgres/services/ignition/config/resources/dev/config-mode.json +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/ignition_stack/templates/standalone-postgres/services/ignition/projects/.gitkeep +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/__init__.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/conftest.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/combos/network-split/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/combos/smoke-stack/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/profiles/hub-and-spoke/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/profiles/mcp-n8n/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/profiles/standalone/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/scaleout-skeleton/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/services/chariot/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/services/db-mariadb/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/services/db-mongo/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/services/db-mysql/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/services/db-postgres/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/services/emqx/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/services/hivemq/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/services/kafka/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/services/keycloak/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/services/modbus-sim/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/services/n8n/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/services/opcua-sim/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/services/rabbitmq/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/golden/standalone-postgres/docker-compose.yaml +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/test_completion.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/test_init_standalone.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/test_modules_catalog.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/test_modules_cli.py +0 -0
- {ignition_stack-0.1.1 → ignition_stack-0.3.0}/tests/test_postsetup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ignition-stack
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.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
|
|
@@ -25,9 +25,6 @@ Requires-Dist: typer>=0.12
|
|
|
25
25
|
Provides-Extra: dev
|
|
26
26
|
Requires-Dist: pytest>=8.3; extra == 'dev'
|
|
27
27
|
Requires-Dist: ruff>=0.7; extra == 'dev'
|
|
28
|
-
Provides-Extra: poc
|
|
29
|
-
Requires-Dist: httpx>=0.27; extra == 'poc'
|
|
30
|
-
Requires-Dist: playwright>=1.49; extra == 'poc'
|
|
31
28
|
Description-Content-Type: text/markdown
|
|
32
29
|
|
|
33
30
|
# ignition-stack
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Catalog of the built-in IA modules that ship inside the Ignition gateway
|
|
2
|
+
# image. Used to translate a gateway's `disable_builtins` slugs into the
|
|
3
|
+
# GATEWAY_MODULES_ENABLED whitelist the engine emits (enabled = all built-ins
|
|
4
|
+
# minus the disabled ones, plus any third-party module identifiers).
|
|
5
|
+
#
|
|
6
|
+
# Why this file exists:
|
|
7
|
+
# GATEWAY_MODULES_ENABLED is a strict WHITELIST, not a blocklist - anything
|
|
8
|
+
# not listed is quarantined at boot (verified live on 8.3.6). So "disable
|
|
9
|
+
# Vision" can only be expressed as "enable every built-in except Vision".
|
|
10
|
+
# That inversion needs the COMPLETE built-in set; an incomplete list would
|
|
11
|
+
# silently quarantine the modules we forgot to enumerate. This file is that
|
|
12
|
+
# complete set, and tests/test_builtin_catalog.py (marked `smoke`) re-derives
|
|
13
|
+
# it from the live image so a stale list fails CI loudly instead of dropping
|
|
14
|
+
# modules silently.
|
|
15
|
+
#
|
|
16
|
+
# Provenance (how to regenerate when bumping the Ignition image):
|
|
17
|
+
# 1. Boot the pinned image with no whitelist so every built-in loads:
|
|
18
|
+
# docker run --rm -e ACCEPT_IGNITION_EULA=Y -e GATEWAY_ADMIN_PASSWORD=x \
|
|
19
|
+
# inductiveautomation/ignition:<tag>
|
|
20
|
+
# 2. Read the loaded set from the gateway log lines:
|
|
21
|
+
# Starting up module '<identifier>' ... module-name=<name>
|
|
22
|
+
# 3. Update `ignition_version` and the `modules` list below to match.
|
|
23
|
+
# The smoke guard test automates this comparison.
|
|
24
|
+
#
|
|
25
|
+
# `identifier` is the fully-qualified module id used verbatim in
|
|
26
|
+
# GATEWAY_MODULES_ENABLED. `slug` is the friendly kebab name a user puts in
|
|
27
|
+
# `disable_builtins`. `name` is the gateway's display name (for wizard labels).
|
|
28
|
+
|
|
29
|
+
version: 1
|
|
30
|
+
|
|
31
|
+
# The exact Ignition image tag this built-in set was captured from. The guard
|
|
32
|
+
# test only asserts a match when the running image reports this version.
|
|
33
|
+
ignition_version: "8.3.6"
|
|
34
|
+
|
|
35
|
+
modules:
|
|
36
|
+
- slug: alarm-notification
|
|
37
|
+
identifier: com.inductiveautomation.alarm-notification
|
|
38
|
+
name: Alarm Notification
|
|
39
|
+
- slug: allen-bradley-driver
|
|
40
|
+
identifier: com.inductiveautomation.opcua.drivers.ablegacy
|
|
41
|
+
name: Allen-Bradley Driver
|
|
42
|
+
- slug: bacnet-driver
|
|
43
|
+
identifier: com.inductiveautomation.opcua.drivers.bacnet
|
|
44
|
+
name: BACnet Driver
|
|
45
|
+
- slug: enterprise-administration
|
|
46
|
+
identifier: com.inductiveautomation.eam
|
|
47
|
+
name: Enterprise Administration
|
|
48
|
+
- slug: event-streams
|
|
49
|
+
identifier: com.inductiveautomation.eventstream
|
|
50
|
+
name: Event Streams
|
|
51
|
+
- slug: historian-core
|
|
52
|
+
identifier: com.inductiveautomation.historian
|
|
53
|
+
name: Historian Core
|
|
54
|
+
- slug: kafka-connector
|
|
55
|
+
identifier: com.inductiveautomation.connectors.kafka
|
|
56
|
+
name: Kafka Connector
|
|
57
|
+
- slug: legacy-dnp3-driver
|
|
58
|
+
identifier: com.inductiveautomation.opcua.drivers.dnp3
|
|
59
|
+
name: Legacy DNP3 Driver
|
|
60
|
+
- slug: logix-driver
|
|
61
|
+
identifier: com.inductiveautomation.opcua.drivers.logix
|
|
62
|
+
name: Logix Driver
|
|
63
|
+
- slug: mariadb-jdbc-driver
|
|
64
|
+
identifier: com.inductiveautomation.jdbc.mariadb
|
|
65
|
+
name: MariaDB JDBC Driver
|
|
66
|
+
- slug: micro800-driver
|
|
67
|
+
identifier: com.inductiveautomation.opcua.drivers.micro800
|
|
68
|
+
name: Micro800 Driver
|
|
69
|
+
- slug: mitsubishi-driver
|
|
70
|
+
identifier: com.inductiveautomation.opcua.drivers.mitsubishi
|
|
71
|
+
name: Mitsubishi Driver
|
|
72
|
+
- slug: modbus-driver
|
|
73
|
+
identifier: com.inductiveautomation.opcua.drivers.modbus
|
|
74
|
+
name: Modbus Driver
|
|
75
|
+
- slug: mssql-jdbc-driver
|
|
76
|
+
identifier: com.inductiveautomation.jdbc.mssql
|
|
77
|
+
name: MSSQL JDBC Driver
|
|
78
|
+
- slug: omron-driver
|
|
79
|
+
identifier: com.inductiveautomation.opcua.drivers.omron
|
|
80
|
+
name: Omron Driver
|
|
81
|
+
- slug: opc-ua
|
|
82
|
+
identifier: com.inductiveautomation.opcua
|
|
83
|
+
name: OPC-UA
|
|
84
|
+
- slug: perspective
|
|
85
|
+
identifier: com.inductiveautomation.perspective
|
|
86
|
+
name: Perspective
|
|
87
|
+
- slug: postgresql-jdbc-driver
|
|
88
|
+
identifier: com.inductiveautomation.jdbc.postgresql
|
|
89
|
+
name: PostgreSQL JDBC Driver
|
|
90
|
+
- slug: reporting
|
|
91
|
+
identifier: com.inductiveautomation.reporting
|
|
92
|
+
name: Reporting
|
|
93
|
+
- slug: sfc
|
|
94
|
+
identifier: com.inductiveautomation.sfc
|
|
95
|
+
name: SFC
|
|
96
|
+
- slug: siemens-drivers
|
|
97
|
+
identifier: com.inductiveautomation.opcua.drivers.siemens
|
|
98
|
+
name: Siemens Drivers
|
|
99
|
+
- slug: siemens-enhanced-driver
|
|
100
|
+
identifier: com.inductiveautomation.opcua.drivers.siemens-symbolic
|
|
101
|
+
name: Siemens Enhanced Driver
|
|
102
|
+
- slug: sms-notification
|
|
103
|
+
identifier: com.inductiveautomation.sms-notification
|
|
104
|
+
name: SMS Notification
|
|
105
|
+
- slug: sql-bridge
|
|
106
|
+
identifier: com.inductiveautomation.sqlbridge
|
|
107
|
+
name: SQL Bridge
|
|
108
|
+
- slug: sql-historian
|
|
109
|
+
identifier: com.inductiveautomation.historian.sql
|
|
110
|
+
name: SQL Historian
|
|
111
|
+
- slug: symbol-factory
|
|
112
|
+
identifier: com.inductiveautomation.symbol-factory
|
|
113
|
+
name: Symbol Factory
|
|
114
|
+
- slug: udp-and-tcp-drivers
|
|
115
|
+
identifier: com.inductiveautomation.opcua.drivers.tcpudp
|
|
116
|
+
name: UDP and TCP Drivers
|
|
117
|
+
- slug: vision
|
|
118
|
+
identifier: com.inductiveautomation.vision
|
|
119
|
+
name: Vision
|
|
120
|
+
- slug: webdev
|
|
121
|
+
identifier: com.inductiveautomation.webdev
|
|
122
|
+
name: WebDev
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.0"
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
"""Load and query the built-in IA module catalog (``builtin_modules.yaml``).
|
|
2
|
+
|
|
3
|
+
The third-party catalog (``modules.yaml`` + ``catalog/schema.py``) covers
|
|
4
|
+
modules the CLI *adds*. This module covers the modules that *already ship*
|
|
5
|
+
inside the gateway image, which the engine needs in order to translate a
|
|
6
|
+
gateway's ``disable_builtins`` slugs into a ``GATEWAY_MODULES_ENABLED``
|
|
7
|
+
whitelist.
|
|
8
|
+
|
|
9
|
+
The whitelist is strict: anything not listed is quarantined at boot. So
|
|
10
|
+
"disable Vision" is expressed as "enable every built-in except Vision",
|
|
11
|
+
which requires the *complete* built-in set - hence a pinned data file plus a
|
|
12
|
+
smoke guard test that re-derives it from the live image.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from functools import lru_cache
|
|
18
|
+
from importlib import resources
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Annotated
|
|
21
|
+
|
|
22
|
+
import yaml
|
|
23
|
+
from pydantic import BaseModel, ConfigDict, Field, ValidationError, field_validator
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BuiltinCatalogLoadError(Exception):
|
|
27
|
+
"""Raised when builtin_modules.yaml cannot be read or fails validation."""
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
DEFAULT_BUILTIN_CATALOG_NAME = "builtin_modules.yaml"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class BuiltinModule(BaseModel):
|
|
34
|
+
"""One built-in IA module that ships inside the gateway image."""
|
|
35
|
+
|
|
36
|
+
model_config = ConfigDict(extra="forbid", frozen=True)
|
|
37
|
+
|
|
38
|
+
slug: Annotated[
|
|
39
|
+
str,
|
|
40
|
+
Field(
|
|
41
|
+
min_length=1,
|
|
42
|
+
pattern=r"^[a-z0-9][a-z0-9-]*$",
|
|
43
|
+
description="Friendly kebab name a user puts in `disable_builtins`.",
|
|
44
|
+
),
|
|
45
|
+
]
|
|
46
|
+
identifier: Annotated[
|
|
47
|
+
str,
|
|
48
|
+
Field(
|
|
49
|
+
min_length=1,
|
|
50
|
+
pattern=r"^[a-z0-9.-]+$",
|
|
51
|
+
description="Fully-qualified module id, used verbatim in GATEWAY_MODULES_ENABLED.",
|
|
52
|
+
),
|
|
53
|
+
]
|
|
54
|
+
name: Annotated[str, Field(min_length=1, description="Gateway display name (wizard label).")]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class BuiltinCatalog(BaseModel):
|
|
58
|
+
"""Top-level shape of builtin_modules.yaml."""
|
|
59
|
+
|
|
60
|
+
model_config = ConfigDict(extra="forbid", frozen=True)
|
|
61
|
+
|
|
62
|
+
version: Annotated[int, Field(ge=1)]
|
|
63
|
+
ignition_version: Annotated[
|
|
64
|
+
str,
|
|
65
|
+
Field(min_length=1, description="Image tag this built-in set was captured from."),
|
|
66
|
+
]
|
|
67
|
+
modules: Annotated[list[BuiltinModule], Field(min_length=1)]
|
|
68
|
+
|
|
69
|
+
@field_validator("modules")
|
|
70
|
+
@classmethod
|
|
71
|
+
def _slugs_unique(cls, modules: list[BuiltinModule]) -> list[BuiltinModule]:
|
|
72
|
+
slugs = [m.slug for m in modules]
|
|
73
|
+
dupes = sorted({s for s in slugs if slugs.count(s) > 1})
|
|
74
|
+
if dupes:
|
|
75
|
+
raise ValueError(f"duplicate built-in slugs: {', '.join(dupes)}")
|
|
76
|
+
return modules
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def slugs(self) -> set[str]:
|
|
80
|
+
"""Every known built-in slug."""
|
|
81
|
+
return {m.slug for m in self.modules}
|
|
82
|
+
|
|
83
|
+
def identifiers_excluding(self, disabled_slugs: list[str]) -> list[str]:
|
|
84
|
+
"""FQ identifiers of every built-in whose slug is not in ``disabled_slugs``.
|
|
85
|
+
|
|
86
|
+
Order follows the catalog (already alphabetical by slug) so generated
|
|
87
|
+
whitelists are deterministic and golden-stable.
|
|
88
|
+
"""
|
|
89
|
+
disabled = set(disabled_slugs)
|
|
90
|
+
return [m.identifier for m in self.modules if m.slug not in disabled]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def load_builtin_catalog(path: Path | None = None) -> BuiltinCatalog:
|
|
94
|
+
"""Load and validate the built-in catalog.
|
|
95
|
+
|
|
96
|
+
With ``path=None`` the catalog shipped with the installed package is used;
|
|
97
|
+
a path overrides it (test fixtures). Mirrors ``catalog.loader.load_catalog``.
|
|
98
|
+
"""
|
|
99
|
+
yaml_text = _read_yaml_text(path)
|
|
100
|
+
name = DEFAULT_BUILTIN_CATALOG_NAME
|
|
101
|
+
try:
|
|
102
|
+
raw = yaml.safe_load(yaml_text)
|
|
103
|
+
except yaml.YAMLError as exc:
|
|
104
|
+
raise BuiltinCatalogLoadError(f"{name} is not valid YAML: {exc}") from exc
|
|
105
|
+
|
|
106
|
+
if not isinstance(raw, dict):
|
|
107
|
+
raise BuiltinCatalogLoadError(f"{name} top-level must be a mapping.")
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
return BuiltinCatalog.model_validate(raw)
|
|
111
|
+
except ValidationError as exc:
|
|
112
|
+
raise BuiltinCatalogLoadError(
|
|
113
|
+
f"{DEFAULT_BUILTIN_CATALOG_NAME} failed schema validation:\n{exc}"
|
|
114
|
+
) from exc
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@lru_cache(maxsize=1)
|
|
118
|
+
def default_builtin_catalog() -> BuiltinCatalog:
|
|
119
|
+
"""The built-in catalog shipped with the package, loaded once and cached.
|
|
120
|
+
|
|
121
|
+
The data file is immutable package data, so both config validation and the
|
|
122
|
+
compose engine can share a single memoized read rather than re-parsing YAML
|
|
123
|
+
on every gateway.
|
|
124
|
+
"""
|
|
125
|
+
return load_builtin_catalog()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def builtin_slugs() -> frozenset[str]:
|
|
129
|
+
"""Slugs of the shipped built-in catalog, for cheap ``disable_builtins`` validation."""
|
|
130
|
+
return frozenset(default_builtin_catalog().slugs)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def validate_disable_slugs(slugs: list[str]) -> None:
|
|
134
|
+
"""Raise ``ValueError`` if any slug is not a known built-in.
|
|
135
|
+
|
|
136
|
+
Shared by ``GatewayConfig`` field validation (construction-time) and
|
|
137
|
+
``profiles.apply_disable_builtins`` (post-construction mutation, which
|
|
138
|
+
pydantic does not re-validate), so the wizard/CLI path is guarded too. A
|
|
139
|
+
typo would otherwise be a silent no-op - the slug just isn't disabled.
|
|
140
|
+
"""
|
|
141
|
+
known = builtin_slugs()
|
|
142
|
+
unknown = [s for s in slugs if s not in known]
|
|
143
|
+
if unknown:
|
|
144
|
+
raise ValueError(
|
|
145
|
+
f"unknown built-in module slug(s): {', '.join(unknown)}. "
|
|
146
|
+
f"Valid slugs are: {', '.join(sorted(known))}"
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _read_yaml_text(path: Path | None) -> str:
|
|
151
|
+
if path is not None:
|
|
152
|
+
if not path.is_file():
|
|
153
|
+
raise BuiltinCatalogLoadError(f"Built-in catalog not found at {path}.")
|
|
154
|
+
return path.read_text(encoding="utf-8")
|
|
155
|
+
|
|
156
|
+
# Installed wheels: force-included as ignition_stack/builtin_modules.yaml.
|
|
157
|
+
# Editable dev installs: it lives at the repo root next to pyproject.toml.
|
|
158
|
+
try:
|
|
159
|
+
bundled = resources.files("ignition_stack").joinpath(DEFAULT_BUILTIN_CATALOG_NAME)
|
|
160
|
+
if bundled.is_file():
|
|
161
|
+
return bundled.read_text(encoding="utf-8")
|
|
162
|
+
except (FileNotFoundError, OSError, ModuleNotFoundError):
|
|
163
|
+
pass
|
|
164
|
+
|
|
165
|
+
repo_root = Path(__file__).resolve().parents[2]
|
|
166
|
+
dev_path = repo_root / DEFAULT_BUILTIN_CATALOG_NAME
|
|
167
|
+
if not dev_path.is_file():
|
|
168
|
+
raise BuiltinCatalogLoadError(
|
|
169
|
+
f"{DEFAULT_BUILTIN_CATALOG_NAME} not found in package data or at {dev_path}."
|
|
170
|
+
)
|
|
171
|
+
return dev_path.read_text(encoding="utf-8")
|