You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ma...@apache.org on 2026/03/31 06:16:57 UTC

(nifi-minifi-cpp) branch apache-rusty updated (d6b304883 -> 7d1677356)

This is an automated email from the ASF dual-hosted git repository.

martinzink pushed a change to branch apache-rusty
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git


 discard d6b304883 fix pyproject.toml
 discard 2526ec186 rename test_framework
 discard 5252c8acc MINIFICPP-2715 - Use a single integer as c api version MINIFICPP-2715 - Add extension loading tests for version verification
 discard 9f2e4cd84 warn
 discard 6e41a7609 MINIFICPP-2731 Allow cancellation from ProcessSession::write
 discard 71e26e126 MINIFICPP-2724 Add MinifiStatus return type to C api function that might fail
 discard 3dbdb934a win docker improvements
 discard 27ec5da47 MINIFICPP-2722 C API enum explicit type
 discard a350fc38d upload msi for easier testing
 discard a6128abb1 MINIFICPP-2718 Windows based docker tests
 discard 6a4c5f7c0 MINIFICPP-2712 Generate modular docs#2097
 discard 2da8c20d0 MINIFICPP-2711 Docker tests, missing logs if container fails to start #2099
 discard d439b84b9 MINIFICPP-2708 Controller Service C API
 discard 2c10e0290 MINIFICPP-2715 - Use symbols to check api compatibility
 discard 0e9b52a31 MINIFICPP-2710 Refactor ControllerServiceNodeMap
 discard fa362fa87 MINIFICPP-2669 - Reduce controller service api
     add d24d32400 MINIFICPP-2683 Move C2 docker tests to modular docker tests
     add e730451c9 MINIFICPP-2685 Move MQTT tests to modular docker tests
     add ebc89c186 MINIFICPP-2256 Remove LoggerConfiguration::newInstance
     add 3b1334649 MINIFICPP-2722 C API enum explicit type
     add 3190ca615 MINIFICPP-2724 Add MinifiStatus return type to C api function that might fail
     add b525cc861 MINIFICPP-2725 python: fix EL with ENVIRONMENT scope
     add e58d829b1 MINIFICPP-2728 upgrade openssl to 3.3.6
     add 71908d19e MINIFICPP-2727 Stabilize verify package tests
     add 7c18354a8 MINIFICPP-2729 Increase timeout for MacOS jobs
     add f856bb60f MINIFICPP-2730 Check if config.yml exists when creating the debug bundle
     add 30fa5d153 MINIFICPP-2726 Fix HTTP S2S no data and error handling
     add 162ccfb55 MINIFICPP-2732 Clear class descriptions when extensions are cleared
     add 4096b8637 MINIFICPP-2733 Fix compilation on Windows in debug mode with Ninja
     add 24933300e MINIFICPP-2734 Remove BinFiles from processor list
     add 2f26cb92f MINIFICPP-2723 Add log and properties.d dir property files to debug bundle
     add b1137d2b7 MINIFICPP-2736 Add checks of library unload result for class description removal
     add e2de4ca72 MINIFICPP-2686 Move Prometheus tests to modular docker tests
     add c460780a9 MINIFICPP-2688 Move site-to-site tests to modular docker tests
     add d39671da8 MINIFICPP-2687 Move Python extension docker tests to modular docker tests
     add 91481eff7 MINIFICPP-2716 Refactor and stabilize modular docker tests
     add 7ec2fd7c1 MINIFICPP-2714 Move Kubernetes to modular docker tests
     add 572832b7e MINIFICPP-2717 Remove old docker testing framework
     add 30c2434bd MINIFICPP-2739 Add test scenario info to behave logs
     add 30493bf09 MINIFICPP-2738 Suppress false positive warning on GCC 12
     add 1d764d7a7 MINIFICPP-2744 Fix ubuntu_22_04_clang_arm CI job
     add 022bd6ffd MINIFICPP-2745 Upgrade OPC UA library to v1.5.2
     add 8f712a67c MINIFICPP-2712 Generate modular docs
     add 6f68543e3 MINIFICPP-2711 Docker tests, missing logs if container fails to start
     add 18dbb8955 MINIFICPP-2747 Fix flaky MQTT tests
     add f6ef57e23 Remove docker/requirements.txt
     add a89a27c9a Bump pyopenssl from 25.0.0 to 26.0.0 in /behave_framework
     add 131f6e4dd MINIFICPP-2669 - Reduce controller service api
     add 4a857ef4a MINIFICPP-2696 Add option for optimizationForSmallDB in DatabaseConte…
     add a30037b94 MINIFICPP-2751 Remove MinifiStatusToString before C Api is finalized
     add 5ccdfd588 MINIFICPP-2753 - Remove restore from c-api
     add 2060d13d8 MINIFICPP-2755 fix macOS CI failure
     add b435c4861 MINIFICPP-2710 Refactor ControllerServiceNodeMap
     add 75d0bbec9 MINIFICPP-2731 Allow cancellation from ProcessSession::write
     add 77720cc3f MINIFICPP-2750 Fix Windows build with CMake 4
     add a415368f1 MINIFICPP-2758 Rename step_impls in minifi behave
     add ad163be29 MINIFICPP-2602 Add withAllowedTypes<> to all propertys that require ControllerServices
     add 20805f0fa MINIFICPP-2757 Build windows artifacts in create-release-artifacts.yml CI job
     add a1f083175 Bump requests from 2.32.4 to 2.33.0 in /github_scripts
     add f53397740 MINIFICPP-2715 - Use symbols to check api compatibility #2105
     add 9fc45a8ca MINIFICPP-2752 - Register processors one-by-one #2150
     new 2494252f0 MINIFICPP-2708 Controller Service C API
     new 05d243fd9 MINIFICPP-2718 Windows based docker tests #2133
     new 7d1677356 MINIFICPP-2756 Rename minifi_test_framework to minifi_behave

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (d6b304883)
            \
             N -- N -- N   refs/heads/apache-rusty (7d1677356)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .clang-format                                      |   4 +-
 .github/workflows/ci.yml                           |  71 +-
 .github/workflows/create-release-artifacts.yml     |  64 +-
 .github/workflows/verify-package.yml               |  74 +-
 .gitignore                                         |   3 +-
 CMakeLists.txt                                     |   2 +-
 CONFIGURE.md                                       |  14 +
 CONTROLLERS.md                                     |   2 +-
 Extensions.md                                      |  21 +-
 PROCESSORS.md                                      |  29 -
 README.md                                          |  20 +-
 behave_framework/.gitignore                        |   2 -
 behave_framework/pyproject.toml                    |   6 +-
 behave_framework/src/minifi_behave/__init__.py     |   0
 .../minifi_behave/containers/container_linux.py    |  63 +-
 .../minifi_behave/containers/container_protocol.py |  27 +-
 .../minifi_behave/containers/container_windows.py  |  52 +-
 .../containers/http_proxy_container.py             |   3 +-
 .../minifi_behave/containers/minifi_container.py   | 202 -----
 .../minifi_behave/containers/minifi_controller.py  |  42 +-
 .../containers/minifi_fhs_container.py             | 119 ---
 .../containers/minifi_linux_container.py           | 105 ++-
 .../minifi_behave/containers/minifi_protocol.py    |  49 ++
 .../containers/minifi_win_container.py             |  16 +
 .../src/minifi_behave/containers/nifi_container.py |  26 +-
 .../resources/nifi}/convert_cert_to_jks.sh         |   0
 behave_framework/src/minifi_behave/core/hooks.py   |  17 +-
 .../src/minifi_behave/core/minifi_test_context.py  |  15 +-
 .../src/minifi_behave/core/ssl_utils.py            | 142 ++-
 .../src/minifi_behave/minifi/connection.py         |   1 +
 .../src/minifi_behave/minifi/flow_definition.py    |  60 ++
 .../minifi/{parameter.py => input_port.py}         |  29 +-
 .../minifi_behave/minifi/minifi_flow_definition.py |   9 +-
 .../minifi_behave/minifi/nifi_flow_definition.py   |  22 +-
 .../minifi/{parameter.py => output_port.py}        |  29 +-
 .../{controller_service.py => remote_port.py}      |  16 +-
 .../minifi_behave/minifi/remote_process_group.py   |  44 +
 .../src/minifi_behave/steps/checking_steps.py      | 115 ++-
 .../src/minifi_behave/steps/configuration_steps.py |  31 +-
 .../src/minifi_behave/steps/core_steps.py          | 120 ++-
 .../src/minifi_behave/steps/flow_building_steps.py | 187 ++--
 cmake/BundledOpen62541.cmake                       |   6 +-
 cmake/BundledOpenSSL.cmake                         |  12 +-
 cmake/DockerConfig.cmake                           |  33 +-
 cmake/ExpressionLanguage.cmake                     |  22 +-
 cmake/VerifyPackageWithDocker.cmake                |  31 +-
 conf/minifi.properties.in                          |   1 +
 .../core/controller/ControllerServiceBase.h        |  27 +-
 core-framework/src/http/HTTPClient.cpp             |   5 +-
 docker/DockerVerify.sh                             | 195 -----
 docker/RunBehaveTests.sh                           |  38 +-
 docker/installed/win.Dockerfile                    |  18 +
 docker/requirements.txt                            |  13 -
 docker/test/integration/.gitignore                 |   2 -
 docker/test/integration/__init__.py                |   0
 docker/test/integration/cluster/ContainerStore.py  | 311 -------
 .../test/integration/cluster/DockerCommunicator.py |  86 --
 .../test/integration/cluster/DockerTestCluster.py  | 283 ------
 .../cluster/DockerTestDirectoryBindings.py         | 221 -----
 docker/test/integration/cluster/FeatureContext.py  |  21 -
 docker/test/integration/cluster/ImageStore.py      | 361 --------
 docker/test/integration/cluster/KubernetesProxy.py | 161 ----
 docker/test/integration/cluster/LogSource.py       |  19 -
 docker/test/integration/cluster/__init__.py        |   0
 .../integration/cluster/checkers/AzureChecker.py   |  59 --
 .../integration/cluster/checkers/ModbusChecker.py  |  24 -
 .../cluster/checkers/PostgresChecker.py            |  27 -
 .../cluster/checkers/PrometheusChecker.py          | 136 ---
 .../test/integration/cluster/checkers/__init__.py  |   0
 .../containers/AzureStorageServerContainer.py      |  40 -
 .../integration/cluster/containers/Container.py    |  90 --
 .../cluster/containers/FlowContainer.py            |  63 --
 .../cluster/containers/HttpProxyContainer.py       |  39 -
 .../containers/MinifiAsPodInKubernetesCluster.py   |  73 --
 .../cluster/containers/MinifiC2ServerContainer.py  | 112 ---
 .../cluster/containers/MinifiContainer.py          | 244 ------
 .../cluster/containers/MqttBrokerContainer.py      |  40 -
 .../cluster/containers/NifiContainer.py            | 100 ---
 .../containers/PostgreSQLServerContainer.py        |  37 -
 .../cluster/containers/PrometheusContainer.py      | 100 ---
 .../cluster/containers/SyslogTcpClientContainer.py |  38 -
 .../cluster/containers/SyslogUdpClientContainer.py |  38 -
 .../cluster/containers/ZookeeperContainer.py       |  39 -
 .../integration/cluster/containers/__init__.py     |   0
 .../features/MiNiFi_integration_test_driver.py     | 399 ---------
 docker/test/integration/features/README.md         |  43 -
 docker/test/integration/features/environment.py    | 105 ---
 .../integration/features/python_script.feature     |  30 -
 docker/test/integration/features/steps/steps.py    | 953 ---------------------
 docker/test/integration/features/utils.py          |  92 --
 .../filesystem_validation/FileSystemObserver.py    |  69 --
 .../filesystem_validation/OutputEventHandler.py    |  64 --
 .../integration/filesystem_validation/__init__.py  |   0
 docker/test/integration/minifi/__init__.py         |   0
 .../minifi/controllers/JsonRecordSetWriter.py      |  24 -
 .../minifi/controllers/JsonTreeReader.py           |  23 -
 .../controllers/KubernetesControllerService.py     |  28 -
 .../integration/minifi/controllers/ODBCService.py  |  27 -
 .../minifi/controllers/SSLContextService.py        |  39 -
 .../integration/minifi/controllers/XMLReader.py    |  21 -
 .../minifi/controllers/XMLRecordSetWriter.py       |  23 -
 .../integration/minifi/controllers/__init__.py     |   0
 docker/test/integration/minifi/core/Connectable.py |  70 --
 .../integration/minifi/core/ControllerService.py   |  40 -
 docker/test/integration/minifi/core/Funnel.py      |  22 -
 docker/test/integration/minifi/core/InputPort.py   |  34 -
 docker/test/integration/minifi/core/OutputPort.py  |  34 -
 docker/test/integration/minifi/core/Processor.py   |  85 --
 .../integration/minifi/core/RemoteProcessGroup.py  |  40 -
 docker/test/integration/minifi/core/__init__.py    |   0
 .../Minifi_flow_json_serializer.py                 | 209 -----
 .../Minifi_flow_yaml_serializer.py                 | 244 ------
 .../Nifi_flow_json_serializer.py                   | 250 ------
 .../minifi/flow_serialization/__init__.py          |   0
 .../minifi/processors/AddPythonAttribute.py        |  22 -
 .../processors/AzureBlobStorageProcessorBase.py    |  33 -
 .../integration/minifi/processors/ChunkDocument.py |  26 -
 .../processors/CollectKubernetesPodMetrics.py      |  25 -
 .../integration/minifi/processors/ConsumeMQTT.py   |  30 -
 .../integration/minifi/processors/ConvertRecord.py |  23 -
 .../minifi/processors/CountingProcessor.py         |  24 -
 .../minifi/processors/CreateFlowFile.py            |  25 -
 .../integration/minifi/processors/CreateNothing.py |  25 -
 .../minifi/processors/DeleteAzureBlobStorage.py    |  25 -
 .../EvaluateExpressionLanguageChecker.py           |  26 -
 .../integration/minifi/processors/ExecuteSQL.py    |  26 -
 .../integration/minifi/processors/ExecuteScript.py |  25 -
 .../ExpressionLanguagePropertyWithValidator.py     |  26 -
 .../minifi/processors/FailureWithAttributes.py     |  26 -
 .../minifi/processors/FailureWithContent.py        |  26 -
 .../minifi/processors/FetchAzureBlobStorage.py     |  25 -
 .../integration/minifi/processors/FetchFile.py     |  26 -
 .../minifi/processors/FetchOPCProcessor.py         |  25 -
 .../processors/GaussianDistributionWithNumpy.py    |  24 -
 .../minifi/processors/GenerateFlowFile.py          |  26 -
 .../test/integration/minifi/processors/GetFile.py  |  29 -
 .../integration/minifi/processors/HashContent.py   |  27 -
 .../integration/minifi/processors/InvokeHTTP.py    |  46 -
 .../minifi/processors/ListAzureBlobStorage.py      |  27 -
 .../test/integration/minifi/processors/ListFile.py |  26 -
 .../integration/minifi/processors/ListenHTTP.py    |  33 -
 .../integration/minifi/processors/ListenSyslog.py  |  27 -
 .../integration/minifi/processors/LogAttribute.py  |  26 -
 .../minifi/processors/LogDynamicProperties.py      |  20 -
 .../minifi/processors/LogOnDestructionProcessor.py |  23 -
 .../minifi/processors/MoveContentToJson.py         |  24 -
 .../processors/NifiStyleLogDynamicProperties.py    |  25 -
 .../integration/minifi/processors/ParseDocument.py |  26 -
 .../processors/ProcessContextInterfaceChecker.py   |  26 -
 .../integration/minifi/processors/PublishMQTT.py   |  30 -
 .../minifi/processors/PutAzureBlobStorage.py       |  25 -
 .../test/integration/minifi/processors/PutFile.py  |  33 -
 .../minifi/processors/PutOPCProcessor.py           |  26 -
 .../test/integration/minifi/processors/PutSQL.py   |  26 -
 .../minifi/processors/QueryDatabaseTable.py        |  25 -
 .../minifi/processors/RelativeImporterProcessor.py |  26 -
 .../minifi/processors/RemoveFlowFile.py            |  24 -
 .../integration/minifi/processors/ReplaceText.py   |  27 -
 .../minifi/processors/RotatingForwarder.py         |  26 -
 .../minifi/processors/RouteOnAttribute.py          |  27 -
 .../integration/minifi/processors/RouteText.py     |  26 -
 .../minifi/processors/RunLlamaCppInference.py      |  26 -
 .../minifi/processors/SetRecordField.py            |  26 -
 .../processors/SpecialPropertyTypeChecker.py       |  26 -
 .../integration/minifi/processors/SplitText.py     |  24 -
 .../test/integration/minifi/processors/TailFile.py |  25 -
 .../minifi/processors/TestStateManager.py          |  25 -
 .../minifi/processors/TransferToOriginal.py        |  26 -
 .../minifi/processors/UpdateAttribute.py           |  26 -
 .../test/integration/minifi/processors/__init__.py |   0
 .../minifi/validators/EmptyFilesOutPutValidator.py |  44 -
 .../minifi/validators/FileOutputValidator.py       |  95 --
 .../minifi/validators/MultiFileOutputValidator.py  |  81 --
 .../NoContentCheckFileNumberValidator.py           |  38 -
 .../minifi/validators/NoFileOutPutValidator.py     |  31 -
 .../validators/NumFileRangeAndFileSizeValidator.py |  36 -
 .../minifi/validators/NumFileRangeValidator.py     |  38 -
 .../minifi/validators/OutputValidator.py           |  27 -
 .../minifi/validators/SegfaultValidator.py         |  25 -
 .../minifi/validators/SingleFileOutputValidator.py |  38 -
 .../validators/SingleJSONFileOutputValidator.py    |  55 --
 .../SingleOrMultiFileOutputRegexValidator.py       |  38 -
 .../validators/SingleOrMultiFileOutputValidator.py |  38 -
 .../test/integration/minifi/validators/__init__.py |   0
 .../kubernetes/minifi-conf/minifi-log.properties   |   3 -
 .../kubernetes/minifi-conf/minifi.properties       |  10 -
 .../resources/minifi/minifi-log.properties         |   4 -
 .../integration/resources/minifi/minifi.properties |   6 -
 .../integration/resources/python/sleep_forever.py  |  23 -
 .../test/integration/ssl_utils/SSL_cert_utils.py   | 161 ----
 docker/test/integration/ssl_utils/__init__.py      |   0
 .../include/api/core/ProcessorImpl.h               |   2 -
 .../cpp-extension-lib/include/api/core/Resource.h  |   3 -
 .../include/api/utils/minifi-c-utils.h             |   6 +
 .../cpp-extension-lib/src/core/ProcessorImpl.cpp   |   4 -
 .../include/controllers/AttributeProviderService.h |   2 +-
 .../include/controllers/RecordSetReader.h          |   2 +-
 .../include/controllers/RecordSetWriter.h          |   2 +-
 .../controllers/keyvalue/KeyValueStateStorage.h    |   4 +-
 .../include/utils/ExtensionInitUtils.h             |   4 +-
 .../include/utils/ProcessorConfigUtils.h           |   2 +-
 extensions/ExtensionInitializer.cpp                |  10 +-
 .../aws/controllerservices/AWSCredentialsService.h |   5 +-
 extensions/aws/processors/AwsProcessor.h           |   1 +
 .../kinesis_server_container.py                    |   0
 .../{steps => containers}/s3_server_container.py   |   2 +-
 extensions/aws/tests/features/kinesis.feature      |   2 +-
 extensions/aws/tests/features/s3.feature           |  38 +-
 extensions/aws/tests/features/steps/steps.py       |  26 +-
 .../AzureStorageCredentialsService.h               |   5 +-
 .../azure/processors/AzureStorageProcessorBase.cpp |   2 +-
 .../azure_server_container.py                      |   0
 extensions/azure/tests/features/steps/steps.py     |  18 +-
 extensions/civetweb/tests/features/http.feature    | 125 +--
 extensions/civetweb/tests/features/https.feature   | 117 +--
 .../controllerservices/CouchbaseClusterService.h   |   5 +-
 .../couchbase/tests/MockCouchbaseClusterService.h  |   1 +
 .../couchbase_server_container.py                  |  10 +-
 extensions/couchbase/tests/features/steps/steps.py |  12 +-
 .../ElasticsearchCredentialsControllerService.h    |   5 +-
 .../elastic_base_container.py                      |   0
 .../elasticsearch_container.py                     |   0
 .../{steps => containers}/opensearch_container.py  |   0
 .../tests/features/elasticsearch.feature           |   6 +-
 .../tests/features/opensearch.feature              |   6 +-
 .../elasticsearch/tests/features/steps/steps.py    |  20 +-
 .../GCPCredentialsControllerService.h              |   5 +-
 .../fake_gcs_server_container.py                   |   0
 .../tests/features/google_cloud_storage.feature    |   2 +-
 extensions/gcp/tests/features/steps/steps.py       |   8 +-
 .../grafana_loki_container.py                      |   4 +-
 .../reverse_proxy_container.py                     |   0
 .../grafana-loki/tests/features/steps/steps.py     |  18 +-
 .../kafka_server_container.py                      |   0
 extensions/kafka/tests/features/steps/steps.py     |  18 +-
 .../KubernetesControllerService.cpp                |   1 -
 .../KubernetesControllerService.h                  |   1 +
 .../processors/CollectKubernetesPodMetrics.h       |   1 +
 .../tests/features/environment.py                  |  21 +-
 .../kubernetes/tests}/features/kubernetes.feature  |  30 +-
 .../tests/features/resources}/daemon.namespace.yml |   0
 .../resources}/hello-world-one.helper-pod.yml      |   0
 .../resources}/hello-world-two.helper-pod.yml      |   0
 .../resources}/metrics-server.dependencies.yml     |   0
 .../tests/features/resources}/minifi.test-pod.yml  |   6 +-
 .../resources}/namespace-reader.clusterrole.yml    |   0
 .../namespace-reader.clusterrolebinding.yml        |   0
 .../features/resources}/pod-reader.clusterrole.yml |   0
 .../resources}/pod-reader.clusterrolebinding.yml   |   0
 .../tests/features/steps/kubernetes_proxy.py       | 164 ++++
 .../steps/minifi_as_pod_in_kubernetes_cluster.py}  |  33 +-
 .../kubernetes/tests/features/steps/steps.py       |  59 ++
 extensions/libarchive/BinFiles.cpp                 |   2 -
 .../llamacpp/processors/ExtensionInitializer.cpp   |  23 +-
 .../features/containers/mqtt_broker_container.py   |  63 ++
 .../tests/features/environment.py                  |  21 +-
 .../mqtt/tests}/features/mqtt.feature              | 406 +++++----
 .../features/resources/publish_mqtt_message.py     |  20 +-
 extensions/mqtt/tests/features/steps/steps.py      |  76 ++
 extensions/opc/include/opc.h                       |   1 +
 extensions/opc/src/opc.cpp                         |  13 +-
 extensions/opc/tests/OpcUaTestServer.h             |   4 +-
 .../opc_ua_server_container.py                     |   6 +-
 extensions/opc/tests/features/opcua.feature        |  65 +-
 .../tests/features/resources/opcua_client_cert.der | Bin 1040 -> 0 bytes
 .../tests/features/resources/opcua_client_key.der  | Bin 1191 -> 0 bytes
 extensions/opc/tests/features/steps/steps.py       |  49 +-
 extensions/opencv/OpenCVLoader.cpp                 |  10 +-
 .../features/containers/prometheus_container.py    |  92 ++
 .../tests/features/environment.py                  |  22 +-
 .../prometheus/tests}/features/prometheus.feature  |  14 +-
 .../tests/features/resources/prometheus_checker.py | 156 ++++
 .../prometheus/tests/features/steps/steps.py       |  73 ++
 .../python/pythonlibloader/PythonLibLoader.cpp     |  12 +-
 extensions/python/pythonloader/PyProcLoader.cpp    |  12 +-
 .../python/pythonprocessors/nifiapi/properties.py  |   8 +-
 extensions/python/tests/features/environment.py    | 124 +++
 .../python/tests}/features/python.feature          | 181 ++--
 .../tests/features/resources}/CreateFlowFile.py    |   0
 .../tests/features/resources}/CreateNothing.py     |   0
 .../EvaluateExpressionLanguageChecker.py           |   0
 .../ExpressionLanguagePropertyWithValidator.py     |   0
 .../features/resources}/FailureWithAttributes.py   |   0
 .../features/resources}/FailureWithContent.py      |   0
 .../features/resources}/LogDynamicProperties.py    |   0
 .../resources}/NifiStyleLogDynamicProperties.py    |   0
 .../resources}/ProcessContextInterfaceChecker.py   |   0
 .../resources}/RelativeImporterProcessor.py        |   0
 .../tests/features/resources}/RotatingForwarder.py |   0
 .../tests/features/resources}/SetRecordField.py    |   0
 .../resources}/SpecialPropertyTypeChecker.py       |   0
 .../tests/features/resources}/TestStateManager.py  |   0
 .../features/resources}/TransferToOriginal.py      |   0
 .../tests/features/resources}/multiplierutils.py   |   0
 .../tests/features/resources}/subtractutils.py     |   0
 extensions/python/tests/features/steps/steps.py    | 122 +++
 .../rocksdb-repos/DatabaseContentRepository.cpp    |  40 +-
 .../controllers/RocksDbStateStorage.cpp            |   1 -
 .../controllers/RocksDbStateStorage.h              |   1 +
 extensions/rocksdb-repos/database/OpenRocksDb.cpp  |  26 +-
 extensions/rocksdb-repos/database/OpenRocksDb.h    |   2 +
 extensions/rocksdb-repos/database/RocksDbUtils.h   |  12 +
 .../tests/DBContentRepositoryTests.cpp             |  41 +
 extensions/sftp/SFTPLoader.cpp                     |  10 +-
 extensions/sftp/tests/tools/SFTPTestServer.cpp     |   2 +-
 extensions/smb/SmbConnectionControllerService.h    |   5 +-
 .../utils/MockSmbConnectionControllerService.h     |   1 +
 .../{steps => containers}/splunk_container.py      |   0
 extensions/splunk/tests/features/splunk.feature    |   4 +-
 extensions/splunk/tests/features/steps/steps.py    |  10 +-
 extensions/sql/CMakeLists.txt                      |   4 +-
 extensions/sql/services/DatabaseService.cpp        |   2 -
 extensions/sql/services/DatabaseService.h          |   4 +-
 extensions/sql/services/ODBCConnector.h            |   1 +
 .../postgress_server_container.py                  |   2 +-
 extensions/sql/tests/features/steps/steps.py       |   8 +-
 extensions/sql/tests/mocks/MockODBCService.h       |   1 +
 .../controllers/JsonRecordSetWriter.h              |   1 +
 .../controllers/JsonTreeReader.cpp                 |   8 +
 .../controllers/JsonTreeReader.h                   |   1 +
 .../controllers/PersistentMapStateStorage.cpp      |   2 -
 .../controllers/PersistentMapStateStorage.h        |   1 +
 .../controllers/VolatileMapStateStorage.cpp        |   1 -
 .../controllers/VolatileMapStateStorage.h          |   2 +
 .../standard-processors/controllers/XMLReader.h    |   1 +
 .../controllers/XMLRecordSetWriter.h               |   1 +
 .../tests/features/attributes_to_json.feature      |   2 +-
 .../{steps => containers}/diag_slave_container.py  |   2 +-
 .../{steps => containers}/syslog_container.py      |   0
 .../{steps => containers}/tcp_client_container.py  |   2 +-
 .../tests/features/core_functionality.feature      |   2 +-
 .../tests/features/environment.py                  |  26 +-
 .../tests/features/evaluate_json_path.feature      |   4 +-
 .../tests/features/file_system_operations.feature  |   4 +-
 .../tests/features/hashcontent.feature             |  12 +-
 .../tests}/features/minifi_c2_server.feature       |  25 +-
 .../tests/features/minifi_controller.feature       |  12 +-
 .../tests/features/repository.feature              |   1 +
 .../tests}/features/s2s.feature                    | 292 +++----
 .../tests/features/split_json.feature              |   2 +-
 .../features/steps/minifi_c2_server_container.py   |  78 ++
 .../features/steps/minifi_controller_steps.py      |  28 +-
 .../resources/minifi-c2-server/authorizations.yaml |   0
 .../resources/minifi-c2-server/c2.properties       |   0
 .../steps}/resources/minifi-c2-server/config.yml   |   0
 .../tests/features/steps/steps.py                  |  72 +-
 .../tests/unit/TailFileTests.cpp                   |   1 +
 github_scripts/requirements.txt                    |   2 +-
 .../controllers/NetworkPrioritizerService.h        |   7 +-
 libminifi/include/controllers/SSLContextService.h  |   3 +-
 .../controllers/UpdatePolicyControllerService.h    |   5 +-
 libminifi/include/core/ProcessContextImpl.h        |   2 +-
 libminifi/include/core/ProcessGroup.h              |   2 +-
 .../include/core/controller/ControllerService.h    |  10 +-
 .../core/controller/ControllerServiceNodeMap.h     |   2 +-
 libminifi/include/core/extension/ApiVersion.h      |   4 +-
 libminifi/include/core/extension/Extension.h       |   9 +-
 .../include/core/extension/ExtensionManager.h      |   6 +
 .../include/core/logging/LoggerConfiguration.h     |   9 +-
 libminifi/include/properties/Configure.h           |   3 +
 libminifi/include/properties/Properties.h          |   3 +-
 libminifi/include/utils/CControllerService.h       |  12 +-
 libminifi/include/utils/CProcessor.h               |   4 +-
 libminifi/src/Configuration.cpp                    |   1 +
 libminifi/src/Configure.cpp                        |  14 +
 libminifi/src/FlowController.cpp                   |  22 +-
 libminifi/src/RemoteProcessGroupPort.cpp           |   2 +-
 libminifi/src/agent/agent_docs.cpp                 |   5 +
 libminifi/src/controllers/SSLContextService.cpp    |   2 -
 libminifi/src/core/ProcessGroup.cpp                |   5 +-
 .../core/controller/ControllerServiceNodeMap.cpp   |  20 +-
 libminifi/src/core/extension/ApiVersion.cpp        |   2 +-
 libminifi/src/core/extension/Extension.cpp         |  42 +-
 libminifi/src/core/extension/ExtensionManager.cpp  |   1 +
 .../src/core/flow/StructuredConfiguration.cpp      |   7 +-
 .../state/nodes/RepositoryMetricsSourceStore.cpp   |   6 +
 libminifi/src/minifi-c.cpp                         | 121 +--
 libminifi/src/sitetosite/HttpSiteToSiteClient.cpp  |  65 +-
 .../integration/C2ControllerEnableFailureTest.cpp  |   5 +-
 libminifi/test/integration/C2DebugBundleTest.cpp   |  63 +-
 .../extension-verification-test/CApiExtension.cpp  |  12 +-
 .../extension-verification-test/CMakeLists.txt     |  30 +-
 .../CppApiExtension.cpp                            |   8 +-
 .../CreateNotCalled.cpp                            |   7 +-
 .../ExtensionVerificationTests.cpp                 |  11 +-
 .../InvalidMissingInitExtension.cpp                |   2 +-
 .../extension-verification-test/dummy-cpp-api.def  |   3 +-
 .../test/libtest/integration/IntegrationBase.cpp   |   3 +-
 .../test/libtest/integration/IntegrationBase.h     |   9 +-
 .../test/libtest/unit/ControllerServiceUtils.h     |   4 +-
 libminifi/test/libtest/unit/MockClasses.h          |  10 +-
 libminifi/test/libtest/unit/ProvenanceTestHelper.h |   4 +-
 libminifi/test/libtest/unit/TestBase.cpp           |   9 +-
 libminifi/test/libtest/unit/TestUtils.cpp          |  39 +
 libminifi/test/libtest/unit/TestUtils.h            |  18 +-
 libminifi/test/unit/ComponentManifestTests.cpp     |   8 +-
 libminifi/test/unit/LogMetricsPublisherTests.cpp   | 206 ++---
 libminifi/test/unit/LoggerTests.cpp                |  84 +-
 libminifi/test/unit/MetricsTests.cpp               |   4 +-
 libminifi/test/unit/ProcessorConfigUtilsTests.cpp  |  13 +-
 minifi-api/include/minifi-c/minifi-c.h             |  21 +-
 minifi-api/include/minifi-cpp/agent/agent_docs.h   |   1 +
 .../controllers/AttributeProviderService.h         |   4 +-
 .../minifi-cpp/controllers/RecordSetReader.h       |   4 +-
 .../minifi-cpp/controllers/RecordSetWriter.h       |   4 +-
 .../controllers/SSLContextServiceInterface.h       |   4 +-
 .../include/minifi-cpp/core/ProcessContext.h       |   4 +-
 .../minifi-cpp/core/RepositoryMetricsSource.h      |   2 +
 .../core/controller/ControllerServiceApi.h         |  15 +-
 ...erviceInterface.h => ControllerServiceHandle.h} |   7 +-
 .../include/minifi-cpp/properties/Configuration.h  |   1 +
 .../include/minifi-cpp/properties/Configure.h      |   3 +
 .../include/minifi-cpp/properties/Properties.h     |   1 +
 minifi-api/minifi-c-api.def                        |   3 +-
 minifi_main/AgentDocs.cpp                          |  62 +-
 run_flake8.sh                                      |   2 +-
 thirdparty/open62541/open62541.patch               |  75 +-
 .../winflexbison/minimum_cmake_version.patch       |  40 +
 418 files changed, 4035 insertions(+), 10547 deletions(-)
 delete mode 100644 behave_framework/src/minifi_behave/__init__.py
 delete mode 100644 behave_framework/src/minifi_behave/containers/minifi_container.py
 delete mode 100644 behave_framework/src/minifi_behave/containers/minifi_fhs_container.py
 rename {docker/test/integration => behave_framework/src/minifi_behave/containers/resources/nifi}/convert_cert_to_jks.sh (100%)
 copy behave_framework/src/minifi_behave/minifi/{parameter.py => input_port.py} (55%)
 copy behave_framework/src/minifi_behave/minifi/{parameter.py => output_port.py} (55%)
 copy behave_framework/src/minifi_behave/minifi/{controller_service.py => remote_port.py} (74%)
 create mode 100644 behave_framework/src/minifi_behave/minifi/remote_process_group.py
 delete mode 100755 docker/DockerVerify.sh
 delete mode 100644 docker/requirements.txt
 delete mode 100644 docker/test/integration/.gitignore
 delete mode 100644 docker/test/integration/__init__.py
 delete mode 100644 docker/test/integration/cluster/ContainerStore.py
 delete mode 100644 docker/test/integration/cluster/DockerCommunicator.py
 delete mode 100644 docker/test/integration/cluster/DockerTestCluster.py
 delete mode 100644 docker/test/integration/cluster/DockerTestDirectoryBindings.py
 delete mode 100644 docker/test/integration/cluster/FeatureContext.py
 delete mode 100644 docker/test/integration/cluster/ImageStore.py
 delete mode 100644 docker/test/integration/cluster/KubernetesProxy.py
 delete mode 100644 docker/test/integration/cluster/LogSource.py
 delete mode 100644 docker/test/integration/cluster/__init__.py
 delete mode 100644 docker/test/integration/cluster/checkers/AzureChecker.py
 delete mode 100644 docker/test/integration/cluster/checkers/ModbusChecker.py
 delete mode 100644 docker/test/integration/cluster/checkers/PostgresChecker.py
 delete mode 100644 docker/test/integration/cluster/checkers/PrometheusChecker.py
 delete mode 100644 docker/test/integration/cluster/checkers/__init__.py
 delete mode 100644 docker/test/integration/cluster/containers/AzureStorageServerContainer.py
 delete mode 100644 docker/test/integration/cluster/containers/Container.py
 delete mode 100644 docker/test/integration/cluster/containers/FlowContainer.py
 delete mode 100644 docker/test/integration/cluster/containers/HttpProxyContainer.py
 delete mode 100644 docker/test/integration/cluster/containers/MinifiAsPodInKubernetesCluster.py
 delete mode 100644 docker/test/integration/cluster/containers/MinifiC2ServerContainer.py
 delete mode 100644 docker/test/integration/cluster/containers/MinifiContainer.py
 delete mode 100644 docker/test/integration/cluster/containers/MqttBrokerContainer.py
 delete mode 100644 docker/test/integration/cluster/containers/NifiContainer.py
 delete mode 100644 docker/test/integration/cluster/containers/PostgreSQLServerContainer.py
 delete mode 100644 docker/test/integration/cluster/containers/PrometheusContainer.py
 delete mode 100644 docker/test/integration/cluster/containers/SyslogTcpClientContainer.py
 delete mode 100644 docker/test/integration/cluster/containers/SyslogUdpClientContainer.py
 delete mode 100644 docker/test/integration/cluster/containers/ZookeeperContainer.py
 delete mode 100644 docker/test/integration/cluster/containers/__init__.py
 delete mode 100644 docker/test/integration/features/MiNiFi_integration_test_driver.py
 delete mode 100644 docker/test/integration/features/README.md
 delete mode 100644 docker/test/integration/features/environment.py
 delete mode 100644 docker/test/integration/features/python_script.feature
 delete mode 100644 docker/test/integration/features/steps/steps.py
 delete mode 100644 docker/test/integration/features/utils.py
 delete mode 100644 docker/test/integration/filesystem_validation/FileSystemObserver.py
 delete mode 100644 docker/test/integration/filesystem_validation/OutputEventHandler.py
 delete mode 100644 docker/test/integration/filesystem_validation/__init__.py
 delete mode 100644 docker/test/integration/minifi/__init__.py
 delete mode 100644 docker/test/integration/minifi/controllers/JsonRecordSetWriter.py
 delete mode 100644 docker/test/integration/minifi/controllers/JsonTreeReader.py
 delete mode 100644 docker/test/integration/minifi/controllers/KubernetesControllerService.py
 delete mode 100644 docker/test/integration/minifi/controllers/ODBCService.py
 delete mode 100644 docker/test/integration/minifi/controllers/SSLContextService.py
 delete mode 100644 docker/test/integration/minifi/controllers/XMLReader.py
 delete mode 100644 docker/test/integration/minifi/controllers/XMLRecordSetWriter.py
 delete mode 100644 docker/test/integration/minifi/controllers/__init__.py
 delete mode 100644 docker/test/integration/minifi/core/Connectable.py
 delete mode 100644 docker/test/integration/minifi/core/ControllerService.py
 delete mode 100644 docker/test/integration/minifi/core/Funnel.py
 delete mode 100644 docker/test/integration/minifi/core/InputPort.py
 delete mode 100644 docker/test/integration/minifi/core/OutputPort.py
 delete mode 100644 docker/test/integration/minifi/core/Processor.py
 delete mode 100644 docker/test/integration/minifi/core/RemoteProcessGroup.py
 delete mode 100644 docker/test/integration/minifi/core/__init__.py
 delete mode 100644 docker/test/integration/minifi/flow_serialization/Minifi_flow_json_serializer.py
 delete mode 100644 docker/test/integration/minifi/flow_serialization/Minifi_flow_yaml_serializer.py
 delete mode 100644 docker/test/integration/minifi/flow_serialization/Nifi_flow_json_serializer.py
 delete mode 100644 docker/test/integration/minifi/flow_serialization/__init__.py
 delete mode 100644 docker/test/integration/minifi/processors/AddPythonAttribute.py
 delete mode 100644 docker/test/integration/minifi/processors/AzureBlobStorageProcessorBase.py
 delete mode 100644 docker/test/integration/minifi/processors/ChunkDocument.py
 delete mode 100644 docker/test/integration/minifi/processors/CollectKubernetesPodMetrics.py
 delete mode 100644 docker/test/integration/minifi/processors/ConsumeMQTT.py
 delete mode 100644 docker/test/integration/minifi/processors/ConvertRecord.py
 delete mode 100644 docker/test/integration/minifi/processors/CountingProcessor.py
 delete mode 100644 docker/test/integration/minifi/processors/CreateFlowFile.py
 delete mode 100644 docker/test/integration/minifi/processors/CreateNothing.py
 delete mode 100644 docker/test/integration/minifi/processors/DeleteAzureBlobStorage.py
 delete mode 100644 docker/test/integration/minifi/processors/EvaluateExpressionLanguageChecker.py
 delete mode 100644 docker/test/integration/minifi/processors/ExecuteSQL.py
 delete mode 100644 docker/test/integration/minifi/processors/ExecuteScript.py
 delete mode 100644 docker/test/integration/minifi/processors/ExpressionLanguagePropertyWithValidator.py
 delete mode 100644 docker/test/integration/minifi/processors/FailureWithAttributes.py
 delete mode 100644 docker/test/integration/minifi/processors/FailureWithContent.py
 delete mode 100644 docker/test/integration/minifi/processors/FetchAzureBlobStorage.py
 delete mode 100644 docker/test/integration/minifi/processors/FetchFile.py
 delete mode 100644 docker/test/integration/minifi/processors/FetchOPCProcessor.py
 delete mode 100644 docker/test/integration/minifi/processors/GaussianDistributionWithNumpy.py
 delete mode 100644 docker/test/integration/minifi/processors/GenerateFlowFile.py
 delete mode 100644 docker/test/integration/minifi/processors/GetFile.py
 delete mode 100644 docker/test/integration/minifi/processors/HashContent.py
 delete mode 100644 docker/test/integration/minifi/processors/InvokeHTTP.py
 delete mode 100644 docker/test/integration/minifi/processors/ListAzureBlobStorage.py
 delete mode 100644 docker/test/integration/minifi/processors/ListFile.py
 delete mode 100644 docker/test/integration/minifi/processors/ListenHTTP.py
 delete mode 100644 docker/test/integration/minifi/processors/ListenSyslog.py
 delete mode 100644 docker/test/integration/minifi/processors/LogAttribute.py
 delete mode 100644 docker/test/integration/minifi/processors/LogDynamicProperties.py
 delete mode 100644 docker/test/integration/minifi/processors/LogOnDestructionProcessor.py
 delete mode 100644 docker/test/integration/minifi/processors/MoveContentToJson.py
 delete mode 100644 docker/test/integration/minifi/processors/NifiStyleLogDynamicProperties.py
 delete mode 100644 docker/test/integration/minifi/processors/ParseDocument.py
 delete mode 100644 docker/test/integration/minifi/processors/ProcessContextInterfaceChecker.py
 delete mode 100644 docker/test/integration/minifi/processors/PublishMQTT.py
 delete mode 100644 docker/test/integration/minifi/processors/PutAzureBlobStorage.py
 delete mode 100644 docker/test/integration/minifi/processors/PutFile.py
 delete mode 100644 docker/test/integration/minifi/processors/PutOPCProcessor.py
 delete mode 100644 docker/test/integration/minifi/processors/PutSQL.py
 delete mode 100644 docker/test/integration/minifi/processors/QueryDatabaseTable.py
 delete mode 100644 docker/test/integration/minifi/processors/RelativeImporterProcessor.py
 delete mode 100644 docker/test/integration/minifi/processors/RemoveFlowFile.py
 delete mode 100644 docker/test/integration/minifi/processors/ReplaceText.py
 delete mode 100644 docker/test/integration/minifi/processors/RotatingForwarder.py
 delete mode 100644 docker/test/integration/minifi/processors/RouteOnAttribute.py
 delete mode 100644 docker/test/integration/minifi/processors/RouteText.py
 delete mode 100644 docker/test/integration/minifi/processors/RunLlamaCppInference.py
 delete mode 100644 docker/test/integration/minifi/processors/SetRecordField.py
 delete mode 100644 docker/test/integration/minifi/processors/SpecialPropertyTypeChecker.py
 delete mode 100644 docker/test/integration/minifi/processors/SplitText.py
 delete mode 100644 docker/test/integration/minifi/processors/TailFile.py
 delete mode 100644 docker/test/integration/minifi/processors/TestStateManager.py
 delete mode 100644 docker/test/integration/minifi/processors/TransferToOriginal.py
 delete mode 100644 docker/test/integration/minifi/processors/UpdateAttribute.py
 delete mode 100644 docker/test/integration/minifi/processors/__init__.py
 delete mode 100644 docker/test/integration/minifi/validators/EmptyFilesOutPutValidator.py
 delete mode 100644 docker/test/integration/minifi/validators/FileOutputValidator.py
 delete mode 100644 docker/test/integration/minifi/validators/MultiFileOutputValidator.py
 delete mode 100644 docker/test/integration/minifi/validators/NoContentCheckFileNumberValidator.py
 delete mode 100644 docker/test/integration/minifi/validators/NoFileOutPutValidator.py
 delete mode 100644 docker/test/integration/minifi/validators/NumFileRangeAndFileSizeValidator.py
 delete mode 100644 docker/test/integration/minifi/validators/NumFileRangeValidator.py
 delete mode 100644 docker/test/integration/minifi/validators/OutputValidator.py
 delete mode 100644 docker/test/integration/minifi/validators/SegfaultValidator.py
 delete mode 100644 docker/test/integration/minifi/validators/SingleFileOutputValidator.py
 delete mode 100644 docker/test/integration/minifi/validators/SingleJSONFileOutputValidator.py
 delete mode 100644 docker/test/integration/minifi/validators/SingleOrMultiFileOutputRegexValidator.py
 delete mode 100644 docker/test/integration/minifi/validators/SingleOrMultiFileOutputValidator.py
 delete mode 100644 docker/test/integration/minifi/validators/__init__.py
 delete mode 100644 docker/test/integration/resources/kubernetes/minifi-conf/minifi-log.properties
 delete mode 100644 docker/test/integration/resources/kubernetes/minifi-conf/minifi.properties
 delete mode 100644 docker/test/integration/resources/minifi/minifi-log.properties
 delete mode 100644 docker/test/integration/resources/minifi/minifi.properties
 delete mode 100644 docker/test/integration/resources/python/sleep_forever.py
 delete mode 100644 docker/test/integration/ssl_utils/SSL_cert_utils.py
 delete mode 100644 docker/test/integration/ssl_utils/__init__.py
 rename extensions/aws/tests/features/{steps => containers}/kinesis_server_container.py (100%)
 rename extensions/aws/tests/features/{steps => containers}/s3_server_container.py (99%)
 rename extensions/azure/tests/features/{steps => containers}/azure_server_container.py (100%)
 rename extensions/couchbase/tests/features/{steps => containers}/couchbase_server_container.py (97%)
 rename extensions/elasticsearch/tests/features/{steps => containers}/elastic_base_container.py (100%)
 rename extensions/elasticsearch/tests/features/{steps => containers}/elasticsearch_container.py (100%)
 rename extensions/elasticsearch/tests/features/{steps => containers}/opensearch_container.py (100%)
 rename extensions/gcp/tests/features/{steps => containers}/fake_gcs_server_container.py (100%)
 rename extensions/grafana-loki/tests/features/{steps => containers}/grafana_loki_container.py (98%)
 copy extensions/grafana-loki/tests/features/{steps => containers}/reverse_proxy_container.py (100%)
 rename extensions/kafka/tests/features/{steps => containers}/kafka_server_container.py (100%)
 copy extensions/{splunk => kubernetes}/tests/features/environment.py (60%)
 rename {docker/test/integration => extensions/kubernetes/tests}/features/kubernetes.feature (76%)
 rename {docker/test/integration/resources/kubernetes/pods-etc => extensions/kubernetes/tests/features/resources}/daemon.namespace.yml (100%)
 rename {docker/test/integration/resources/kubernetes/pods-etc => extensions/kubernetes/tests/features/resources}/hello-world-one.helper-pod.yml (100%)
 rename {docker/test/integration/resources/kubernetes/pods-etc => extensions/kubernetes/tests/features/resources}/hello-world-two.helper-pod.yml (100%)
 rename {docker/test/integration/resources/kubernetes/pods-etc => extensions/kubernetes/tests/features/resources}/metrics-server.dependencies.yml (100%)
 rename {docker/test/integration/resources/kubernetes/pods-etc => extensions/kubernetes/tests/features/resources}/minifi.test-pod.yml (87%)
 rename {docker/test/integration/resources/kubernetes/pods-etc => extensions/kubernetes/tests/features/resources}/namespace-reader.clusterrole.yml (100%)
 rename {docker/test/integration/resources/kubernetes/pods-etc => extensions/kubernetes/tests/features/resources}/namespace-reader.clusterrolebinding.yml (100%)
 rename {docker/test/integration/resources/kubernetes/pods-etc => extensions/kubernetes/tests/features/resources}/pod-reader.clusterrole.yml (100%)
 rename {docker/test/integration/resources/kubernetes/pods-etc => extensions/kubernetes/tests/features/resources}/pod-reader.clusterrolebinding.yml (100%)
 create mode 100644 extensions/kubernetes/tests/features/steps/kubernetes_proxy.py
 rename extensions/{grafana-loki/tests/features/steps/reverse_proxy_container.py => kubernetes/tests/features/steps/minifi_as_pod_in_kubernetes_cluster.py} (50%)
 create mode 100644 extensions/kubernetes/tests/features/steps/steps.py
 create mode 100644 extensions/mqtt/tests/features/containers/mqtt_broker_container.py
 copy extensions/{grafana-loki => mqtt}/tests/features/environment.py (71%)
 rename {docker/test/integration => extensions/mqtt/tests}/features/mqtt.feature (59%)
 rename docker/test/integration/cluster/checkers/MqttHelper.py => extensions/mqtt/tests/features/resources/publish_mqtt_message.py (62%)
 create mode 100644 extensions/mqtt/tests/features/steps/steps.py
 rename extensions/opc/tests/features/{steps => containers}/opc_ua_server_container.py (87%)
 delete mode 100644 extensions/opc/tests/features/resources/opcua_client_cert.der
 delete mode 100644 extensions/opc/tests/features/resources/opcua_client_key.der
 create mode 100644 extensions/prometheus/tests/features/containers/prometheus_container.py
 copy extensions/{grafana-loki => prometheus}/tests/features/environment.py (69%)
 rename {docker/test/integration => extensions/prometheus/tests}/features/prometheus.feature (91%)
 create mode 100644 extensions/prometheus/tests/features/resources/prometheus_checker.py
 create mode 100644 extensions/prometheus/tests/features/steps/steps.py
 create mode 100644 extensions/python/tests/features/environment.py
 rename {docker/test/integration => extensions/python/tests}/features/python.feature (62%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/CreateFlowFile.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/CreateNothing.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/EvaluateExpressionLanguageChecker.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/ExpressionLanguagePropertyWithValidator.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/FailureWithAttributes.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/FailureWithContent.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/LogDynamicProperties.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/NifiStyleLogDynamicProperties.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/ProcessContextInterfaceChecker.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/RelativeImporterProcessor.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/RotatingForwarder.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/SetRecordField.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/SpecialPropertyTypeChecker.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/TestStateManager.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/TransferToOriginal.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/multiplierutils.py (100%)
 rename {docker/test/integration/resources/python => extensions/python/tests/features/resources}/subtractutils.py (100%)
 create mode 100644 extensions/python/tests/features/steps/steps.py
 rename extensions/splunk/tests/features/{steps => containers}/splunk_container.py (100%)
 rename extensions/sql/tests/features/{steps => containers}/postgress_server_container.py (99%)
 rename extensions/standard-processors/tests/features/{steps => containers}/diag_slave_container.py (98%)
 rename extensions/standard-processors/tests/features/{steps => containers}/syslog_container.py (100%)
 rename extensions/standard-processors/tests/features/{steps => containers}/tcp_client_container.py (98%)
 rename {docker/test/integration => extensions/standard-processors/tests}/features/minifi_c2_server.feature (73%)
 rename {docker/test/integration => extensions/standard-processors/tests}/features/s2s.feature (50%)
 create mode 100644 extensions/standard-processors/tests/features/steps/minifi_c2_server_container.py
 rename {docker/test/integration => extensions/standard-processors/tests/features/steps}/resources/minifi-c2-server/authorizations.yaml (100%)
 rename {docker/test/integration => extensions/standard-processors/tests/features/steps}/resources/minifi-c2-server/c2.properties (100%)
 rename {docker/test/integration => extensions/standard-processors/tests/features/steps}/resources/minifi-c2-server/config.yml (100%)
 rename minifi-api/include/minifi-cpp/core/controller/{ControllerServiceInterface.h => ControllerServiceHandle.h} (79%)
 create mode 100644 thirdparty/winflexbison/minimum_cmake_version.patch


(nifi-minifi-cpp) 03/03: MINIFICPP-2756 Rename minifi_test_framework to minifi_behave

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

martinzink pushed a commit to branch apache-rusty
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git

commit 7d1677356b052de7c31fa1acec2ec22c9b47c3ff
Author: Martin Zink <ma...@apache.org>
AuthorDate: Wed Mar 25 14:36:07 2026 +0100

    MINIFICPP-2756 Rename minifi_test_framework to minifi_behave
---
 behave_framework/.gitignore                              |  2 +-
 behave_framework/pyproject.toml                          |  4 ++--
 .../containers/__init__.py                               |  0
 .../containers/container_linux.py                        | 10 +++++-----
 .../containers/container_protocol.py                     |  6 +++---
 .../containers/container_windows.py                      | 10 +++++-----
 .../containers/directory.py                              |  0
 .../containers/docker_image_builder.py                   |  0
 .../containers/file.py                                   |  0
 .../containers/host_file.py                              |  0
 .../containers/http_proxy_container.py                   |  8 ++++----
 .../containers/minifi_controller.py                      |  2 +-
 .../containers/minifi_linux_container.py                 | 12 ++++++------
 .../containers/minifi_protocol.py                        |  0
 .../containers/minifi_win_container.py                   |  6 +++---
 .../containers/nifi_container.py                         | 16 ++++++++--------
 .../containers/resources/minifi-controller/config.yml    |  0
 .../containers/resources/nifi/convert_cert_to_jks.sh     |  0
 .../core/__init__.py                                     |  0
 .../core/helpers.py                                      |  2 +-
 .../core/hooks.py                                        |  4 ++--
 .../core/minifi_test_context.py                          | 10 +++++-----
 .../core/ssl_utils.py                                    |  0
 .../minifi/__init__.py                                   |  0
 .../minifi/connection.py                                 |  0
 .../minifi/controller_service.py                         |  0
 .../minifi/flow_definition.py                            |  0
 .../minifi/funnel.py                                     |  0
 .../minifi/input_port.py                                 |  0
 .../minifi/minifi_flow_definition.py                     |  0
 .../minifi/nifi_flow_definition.py                       |  0
 .../minifi/output_port.py                                |  0
 .../minifi/parameter.py                                  |  0
 .../minifi/parameter_context.py                          |  2 +-
 .../minifi/processor.py                                  |  0
 .../minifi/remote_port.py                                |  0
 .../minifi/remote_process_group.py                       |  0
 .../steps/__init__.py                                    |  0
 .../steps/checking_steps.py                              |  6 +++---
 .../steps/configuration_steps.py                         | 10 +++++-----
 .../steps/core_steps.py                                  | 10 +++++-----
 .../steps/flow_building_steps.py                         | 14 +++++++-------
 .../features/containers/kinesis_server_container.py      |  8 ++++----
 .../aws/tests/features/containers/s3_server_container.py |  6 +++---
 extensions/aws/tests/features/environment.py             |  4 ++--
 extensions/aws/tests/features/steps/steps.py             | 16 ++++++++--------
 .../tests/features/containers/azure_server_container.py  |  8 ++++----
 extensions/azure/tests/features/environment.py           |  4 ++--
 extensions/azure/tests/features/steps/steps.py           | 14 +++++++-------
 extensions/civetweb/tests/features/environment.py        |  4 ++--
 extensions/civetweb/tests/features/steps/steps.py        |  8 ++++----
 .../features/containers/couchbase_server_container.py    | 10 +++++-----
 extensions/couchbase/tests/features/environment.py       |  6 +++---
 extensions/couchbase/tests/features/steps/steps.py       | 16 ++++++++--------
 .../tests/features/containers/elastic_base_container.py  |  6 +++---
 .../tests/features/containers/elasticsearch_container.py |  8 ++++----
 .../tests/features/containers/opensearch_container.py    |  8 ++++----
 extensions/elasticsearch/tests/features/environment.py   |  4 ++--
 extensions/elasticsearch/tests/features/steps/steps.py   | 14 +++++++-------
 .../features/containers/fake_gcs_server_container.py     |  8 ++++----
 extensions/gcp/tests/features/environment.py             |  4 ++--
 extensions/gcp/tests/features/steps/steps.py             | 12 ++++++------
 .../tests/features/containers/grafana_loki_container.py  | 10 +++++-----
 .../tests/features/containers/reverse_proxy_container.py |  6 +++---
 extensions/grafana-loki/tests/features/environment.py    |  6 +++---
 extensions/grafana-loki/tests/features/steps/steps.py    | 12 ++++++------
 .../tests/features/containers/kafka_server_container.py  | 10 +++++-----
 extensions/kafka/tests/features/environment.py           |  6 +++---
 extensions/kafka/tests/features/steps/steps.py           | 12 ++++++------
 extensions/kubernetes/tests/features/environment.py      |  2 +-
 .../steps/minifi_as_pod_in_kubernetes_cluster.py         |  4 ++--
 extensions/kubernetes/tests/features/steps/steps.py      | 16 ++++++++--------
 extensions/llamacpp/tests/features/environment.py        | 10 +++++-----
 extensions/llamacpp/tests/features/steps/steps.py        |  8 ++++----
 extensions/lua/tests/features/environment.py             |  4 ++--
 extensions/lua/tests/features/steps/steps.py             |  8 ++++----
 .../tests/features/containers/mqtt_broker_container.py   |  8 ++++----
 extensions/mqtt/tests/features/environment.py            |  6 +++---
 extensions/mqtt/tests/features/steps/steps.py            | 14 +++++++-------
 .../tests/features/containers/opc_ua_server_container.py |  6 +++---
 extensions/opc/tests/features/environment.py             |  4 ++--
 extensions/opc/tests/features/steps/steps.py             | 12 ++++++------
 .../tests/features/containers/prometheus_container.py    | 12 ++++++------
 extensions/prometheus/tests/features/environment.py      |  6 +++---
 extensions/prometheus/tests/features/steps/steps.py      | 12 ++++++------
 extensions/python/tests/features/environment.py          |  6 +++---
 extensions/python/tests/features/steps/steps.py          | 12 ++++++------
 .../splunk/tests/features/containers/splunk_container.py | 10 +++++-----
 extensions/splunk/tests/features/environment.py          |  4 ++--
 extensions/splunk/tests/features/steps/steps.py          | 12 ++++++------
 .../features/containers/postgress_server_container.py    |  8 ++++----
 extensions/sql/tests/features/environment.py             |  8 ++++----
 extensions/sql/tests/features/steps/steps.py             | 14 +++++++-------
 .../tests/features/containers/diag_slave_container.py    |  8 ++++----
 .../tests/features/containers/syslog_container.py        |  2 +-
 .../tests/features/containers/tcp_client_container.py    |  6 +++---
 .../standard-processors/tests/features/environment.py    |  4 ++--
 .../tests/features/steps/minifi_c2_server_container.py   | 14 +++++++-------
 .../tests/features/steps/minifi_controller_steps.py      |  4 ++--
 .../standard-processors/tests/features/steps/steps.py    | 14 +++++++-------
 100 files changed, 311 insertions(+), 311 deletions(-)

diff --git a/behave_framework/.gitignore b/behave_framework/.gitignore
index 7437a57e5..7b610c7b5 100644
--- a/behave_framework/.gitignore
+++ b/behave_framework/.gitignore
@@ -2,4 +2,4 @@ venv
 output
 .venv
 .idea
-src/minifi_test_framework.egg-info
+src/minifi_behave.egg-info
diff --git a/behave_framework/pyproject.toml b/behave_framework/pyproject.toml
index 8bb2a83e1..8c398890e 100644
--- a/behave_framework/pyproject.toml
+++ b/behave_framework/pyproject.toml
@@ -1,5 +1,5 @@
 [project]
-name = "minifi-test-framework"
+name = "minifi-behave"
 version = "0.2.0"
 requires-python = ">= 3.10"
 description = "A testing framework for MiNiFi extensions."
@@ -17,4 +17,4 @@ package-dir = {"" = "src"}
 
 [tool.setuptools.packages.find]
 where = ["src"]
-include = ["minifi_test_framework*"]
+include = ["minifi_behave*"]
diff --git a/behave_framework/src/minifi_test_framework/containers/__init__.py b/behave_framework/src/minifi_behave/containers/__init__.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/containers/__init__.py
rename to behave_framework/src/minifi_behave/containers/__init__.py
diff --git a/behave_framework/src/minifi_test_framework/containers/container_linux.py b/behave_framework/src/minifi_behave/containers/container_linux.py
similarity index 98%
rename from behave_framework/src/minifi_test_framework/containers/container_linux.py
rename to behave_framework/src/minifi_behave/containers/container_linux.py
index 6d49a104d..f1161acfc 100644
--- a/behave_framework/src/minifi_test_framework/containers/container_linux.py
+++ b/behave_framework/src/minifi_behave/containers/container_linux.py
@@ -24,16 +24,16 @@ import tarfile
 import tempfile
 import uuid
 from docker.models.networks import Network
-from minifi_test_framework.containers.container_protocol import ContainerProtocol
-from minifi_test_framework.containers.directory import Directory
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.containers.host_file import HostFile
+from minifi_behave.containers.container_protocol import ContainerProtocol
+from minifi_behave.containers.directory import Directory
+from minifi_behave.containers.file import File
+from minifi_behave.containers.host_file import HostFile
 from typing import TYPE_CHECKING
 
 import docker
 
 if TYPE_CHECKING:
-    from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+    from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class LinuxContainer(ContainerProtocol):
diff --git a/behave_framework/src/minifi_test_framework/containers/container_protocol.py b/behave_framework/src/minifi_behave/containers/container_protocol.py
similarity index 90%
rename from behave_framework/src/minifi_test_framework/containers/container_protocol.py
rename to behave_framework/src/minifi_behave/containers/container_protocol.py
index fdbdf2861..bcc537548 100644
--- a/behave_framework/src/minifi_test_framework/containers/container_protocol.py
+++ b/behave_framework/src/minifi_behave/containers/container_protocol.py
@@ -15,9 +15,9 @@
 
 from typing import Protocol, List
 
-from minifi_test_framework.containers.directory import Directory
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.containers.host_file import HostFile
+from minifi_behave.containers.directory import Directory
+from minifi_behave.containers.file import File
+from minifi_behave.containers.host_file import HostFile
 
 
 class ContainerProtocol(Protocol):
diff --git a/behave_framework/src/minifi_test_framework/containers/container_windows.py b/behave_framework/src/minifi_behave/containers/container_windows.py
similarity index 97%
rename from behave_framework/src/minifi_test_framework/containers/container_windows.py
rename to behave_framework/src/minifi_behave/containers/container_windows.py
index c1a9c755a..022948143 100644
--- a/behave_framework/src/minifi_test_framework/containers/container_windows.py
+++ b/behave_framework/src/minifi_behave/containers/container_windows.py
@@ -27,13 +27,13 @@ import docker
 from docker.models.networks import Network
 from docker.models.containers import Container
 
-from minifi_test_framework.containers.container_protocol import ContainerProtocol
-from minifi_test_framework.containers.directory import Directory
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.containers.host_file import HostFile
+from minifi_behave.containers.container_protocol import ContainerProtocol
+from minifi_behave.containers.directory import Directory
+from minifi_behave.containers.file import File
+from minifi_behave.containers.host_file import HostFile
 
 if TYPE_CHECKING:
-    from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+    from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class WindowsContainer(ContainerProtocol):
diff --git a/behave_framework/src/minifi_test_framework/containers/directory.py b/behave_framework/src/minifi_behave/containers/directory.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/containers/directory.py
rename to behave_framework/src/minifi_behave/containers/directory.py
diff --git a/behave_framework/src/minifi_test_framework/containers/docker_image_builder.py b/behave_framework/src/minifi_behave/containers/docker_image_builder.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/containers/docker_image_builder.py
rename to behave_framework/src/minifi_behave/containers/docker_image_builder.py
diff --git a/behave_framework/src/minifi_test_framework/containers/file.py b/behave_framework/src/minifi_behave/containers/file.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/containers/file.py
rename to behave_framework/src/minifi_behave/containers/file.py
diff --git a/behave_framework/src/minifi_test_framework/containers/host_file.py b/behave_framework/src/minifi_behave/containers/host_file.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/containers/host_file.py
rename to behave_framework/src/minifi_behave/containers/host_file.py
diff --git a/behave_framework/src/minifi_test_framework/containers/http_proxy_container.py b/behave_framework/src/minifi_behave/containers/http_proxy_container.py
similarity index 90%
rename from behave_framework/src/minifi_test_framework/containers/http_proxy_container.py
rename to behave_framework/src/minifi_behave/containers/http_proxy_container.py
index 430e2abff..6045662eb 100644
--- a/behave_framework/src/minifi_test_framework/containers/http_proxy_container.py
+++ b/behave_framework/src/minifi_behave/containers/http_proxy_container.py
@@ -17,10 +17,10 @@
 
 from textwrap import dedent
 
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class HttpProxy(LinuxContainer):
diff --git a/behave_framework/src/minifi_test_framework/containers/minifi_controller.py b/behave_framework/src/minifi_behave/containers/minifi_controller.py
similarity index 98%
rename from behave_framework/src/minifi_test_framework/containers/minifi_controller.py
rename to behave_framework/src/minifi_behave/containers/minifi_controller.py
index 59f6e4028..756cbc389 100644
--- a/behave_framework/src/minifi_test_framework/containers/minifi_controller.py
+++ b/behave_framework/src/minifi_behave/containers/minifi_controller.py
@@ -14,7 +14,7 @@
 # limitations under the License.
 
 import logging
-from minifi_test_framework.core.helpers import retry_check
+from minifi_behave.core.helpers import retry_check
 
 
 class MinifiController:
diff --git a/behave_framework/src/minifi_test_framework/containers/minifi_linux_container.py b/behave_framework/src/minifi_behave/containers/minifi_linux_container.py
similarity index 94%
rename from behave_framework/src/minifi_test_framework/containers/minifi_linux_container.py
rename to behave_framework/src/minifi_behave/containers/minifi_linux_container.py
index 62311d9ab..3d012d6df 100644
--- a/behave_framework/src/minifi_test_framework/containers/minifi_linux_container.py
+++ b/behave_framework/src/minifi_behave/containers/minifi_linux_container.py
@@ -17,13 +17,13 @@
 
 import logging
 import os
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.containers.host_file import HostFile
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.core.ssl_utils import make_cert_without_extended_usage, make_client_cert, make_server_cert, \
+from minifi_behave.containers.file import File
+from minifi_behave.containers.host_file import HostFile
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.ssl_utils import make_cert_without_extended_usage, make_client_cert, make_server_cert, \
     dump_cert, dump_key
-from minifi_test_framework.minifi.minifi_flow_definition import MinifiFlowDefinition
+from minifi_behave.minifi.minifi_flow_definition import MinifiFlowDefinition
 from pathlib import Path
 
 from .container_linux import LinuxContainer
diff --git a/behave_framework/src/minifi_test_framework/containers/minifi_protocol.py b/behave_framework/src/minifi_behave/containers/minifi_protocol.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/containers/minifi_protocol.py
rename to behave_framework/src/minifi_behave/containers/minifi_protocol.py
diff --git a/behave_framework/src/minifi_test_framework/containers/minifi_win_container.py b/behave_framework/src/minifi_behave/containers/minifi_win_container.py
similarity index 93%
rename from behave_framework/src/minifi_test_framework/containers/minifi_win_container.py
rename to behave_framework/src/minifi_behave/containers/minifi_win_container.py
index d39216bd3..3aa9407bc 100644
--- a/behave_framework/src/minifi_test_framework/containers/minifi_win_container.py
+++ b/behave_framework/src/minifi_behave/containers/minifi_win_container.py
@@ -16,9 +16,9 @@
 
 from typing import Dict
 
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.minifi.minifi_flow_definition import MinifiFlowDefinition
-from minifi_test_framework.containers.directory import Directory
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.minifi.minifi_flow_definition import MinifiFlowDefinition
+from minifi_behave.containers.directory import Directory
 from .container_windows import WindowsContainer
 from .minifi_protocol import MinifiProtocol
 import logging
diff --git a/behave_framework/src/minifi_test_framework/containers/nifi_container.py b/behave_framework/src/minifi_behave/containers/nifi_container.py
similarity index 91%
rename from behave_framework/src/minifi_test_framework/containers/nifi_container.py
rename to behave_framework/src/minifi_behave/containers/nifi_container.py
index 3d589a8fe..8e36c4a1b 100644
--- a/behave_framework/src/minifi_test_framework/containers/nifi_container.py
+++ b/behave_framework/src/minifi_behave/containers/nifi_container.py
@@ -19,15 +19,15 @@ import logging
 import os
 from pathlib import Path
 
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.minifi.nifi_flow_definition import NifiFlowDefinition
-from minifi_test_framework.containers.host_file import HostFile
-from minifi_test_framework.core.ssl_utils import make_server_cert
+from minifi_behave.containers.file import File
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.minifi.nifi_flow_definition import NifiFlowDefinition
+from minifi_behave.containers.host_file import HostFile
+from minifi_behave.core.ssl_utils import make_server_cert
 
-from minifi_test_framework.core.ssl_utils import dump_cert, dump_key
+from minifi_behave.core.ssl_utils import dump_cert, dump_key
 
 
 class NifiContainer(LinuxContainer):
diff --git a/behave_framework/src/minifi_test_framework/containers/resources/minifi-controller/config.yml b/behave_framework/src/minifi_behave/containers/resources/minifi-controller/config.yml
similarity index 100%
rename from behave_framework/src/minifi_test_framework/containers/resources/minifi-controller/config.yml
rename to behave_framework/src/minifi_behave/containers/resources/minifi-controller/config.yml
diff --git a/behave_framework/src/minifi_test_framework/containers/resources/nifi/convert_cert_to_jks.sh b/behave_framework/src/minifi_behave/containers/resources/nifi/convert_cert_to_jks.sh
similarity index 100%
rename from behave_framework/src/minifi_test_framework/containers/resources/nifi/convert_cert_to_jks.sh
rename to behave_framework/src/minifi_behave/containers/resources/nifi/convert_cert_to_jks.sh
diff --git a/behave_framework/src/minifi_test_framework/core/__init__.py b/behave_framework/src/minifi_behave/core/__init__.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/core/__init__.py
rename to behave_framework/src/minifi_behave/core/__init__.py
diff --git a/behave_framework/src/minifi_test_framework/core/helpers.py b/behave_framework/src/minifi_behave/core/helpers.py
similarity index 98%
rename from behave_framework/src/minifi_test_framework/core/helpers.py
rename to behave_framework/src/minifi_behave/core/helpers.py
index 7b7992056..128aa4e01 100644
--- a/behave_framework/src/minifi_test_framework/core/helpers.py
+++ b/behave_framework/src/minifi_behave/core/helpers.py
@@ -23,7 +23,7 @@ import functools
 from collections.abc import Callable
 
 import docker
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 def log_due_to_failure(context: MinifiTestContext | None):
diff --git a/behave_framework/src/minifi_test_framework/core/hooks.py b/behave_framework/src/minifi_behave/core/hooks.py
similarity index 96%
rename from behave_framework/src/minifi_test_framework/core/hooks.py
rename to behave_framework/src/minifi_behave/core/hooks.py
index ea440cc30..b3073aacc 100644
--- a/behave_framework/src/minifi_test_framework/core/hooks.py
+++ b/behave_framework/src/minifi_behave/core/hooks.py
@@ -23,8 +23,8 @@ from pathlib import Path
 from behave.model import Scenario
 from behave.runner import Context
 
-from minifi_test_framework.core.ssl_utils import make_self_signed_cert
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.ssl_utils import make_self_signed_cert
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 def get_minifi_container_image():
diff --git a/behave_framework/src/minifi_test_framework/core/minifi_test_context.py b/behave_framework/src/minifi_behave/core/minifi_test_context.py
similarity index 81%
rename from behave_framework/src/minifi_test_framework/core/minifi_test_context.py
rename to behave_framework/src/minifi_behave/core/minifi_test_context.py
index 85970b101..5933264d0 100644
--- a/behave_framework/src/minifi_test_framework/core/minifi_test_context.py
+++ b/behave_framework/src/minifi_behave/core/minifi_test_context.py
@@ -24,8 +24,8 @@ from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
 from cryptography.x509 import Certificate
 from docker.models.networks import Network
 
-from minifi_test_framework.containers.container_protocol import ContainerProtocol
-from minifi_test_framework.containers.minifi_protocol import MinifiProtocol
+from minifi_behave.containers.container_protocol import ContainerProtocol
+from minifi_behave.containers.minifi_protocol import MinifiProtocol
 
 
 DEFAULT_MINIFI_CONTAINER_NAME = "minifi-primary"
@@ -47,13 +47,13 @@ class MinifiTestContext(Context):
     def get_or_create_minifi_container(self, container_name: str) -> MinifiContainer:
         if container_name not in self.containers:
             if os.name == 'nt':
-                from minifi_test_framework.containers.minifi_win_container import MinifiWindowsContainer
+                from minifi_behave.containers.minifi_win_container import MinifiWindowsContainer
                 minifi_container = MinifiWindowsContainer(container_name, self)
             elif 'MINIFI_INSTALLATION_TYPE=FHS' in str(docker.from_env().images.get(self.minifi_container_image).history()):
-                from minifi_test_framework.containers.minifi_linux_container import MinifiLinuxContainer, FHSDeployment
+                from minifi_behave.containers.minifi_linux_container import MinifiLinuxContainer, FHSDeployment
                 minifi_container = MinifiLinuxContainer(container_name, self, FHSDeployment())
             else:
-                from minifi_test_framework.containers.minifi_linux_container import MinifiLinuxContainer, NormalDeployment
+                from minifi_behave.containers.minifi_linux_container import MinifiLinuxContainer, NormalDeployment
                 minifi_container = MinifiLinuxContainer(container_name, self, NormalDeployment())
             self.containers[container_name] = minifi_container
         return self.containers[container_name]
diff --git a/behave_framework/src/minifi_test_framework/core/ssl_utils.py b/behave_framework/src/minifi_behave/core/ssl_utils.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/core/ssl_utils.py
rename to behave_framework/src/minifi_behave/core/ssl_utils.py
diff --git a/behave_framework/src/minifi_test_framework/minifi/__init__.py b/behave_framework/src/minifi_behave/minifi/__init__.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/minifi/__init__.py
rename to behave_framework/src/minifi_behave/minifi/__init__.py
diff --git a/behave_framework/src/minifi_test_framework/minifi/connection.py b/behave_framework/src/minifi_behave/minifi/connection.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/minifi/connection.py
rename to behave_framework/src/minifi_behave/minifi/connection.py
diff --git a/behave_framework/src/minifi_test_framework/minifi/controller_service.py b/behave_framework/src/minifi_behave/minifi/controller_service.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/minifi/controller_service.py
rename to behave_framework/src/minifi_behave/minifi/controller_service.py
diff --git a/behave_framework/src/minifi_test_framework/minifi/flow_definition.py b/behave_framework/src/minifi_behave/minifi/flow_definition.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/minifi/flow_definition.py
rename to behave_framework/src/minifi_behave/minifi/flow_definition.py
diff --git a/behave_framework/src/minifi_test_framework/minifi/funnel.py b/behave_framework/src/minifi_behave/minifi/funnel.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/minifi/funnel.py
rename to behave_framework/src/minifi_behave/minifi/funnel.py
diff --git a/behave_framework/src/minifi_test_framework/minifi/input_port.py b/behave_framework/src/minifi_behave/minifi/input_port.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/minifi/input_port.py
rename to behave_framework/src/minifi_behave/minifi/input_port.py
diff --git a/behave_framework/src/minifi_test_framework/minifi/minifi_flow_definition.py b/behave_framework/src/minifi_behave/minifi/minifi_flow_definition.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/minifi/minifi_flow_definition.py
rename to behave_framework/src/minifi_behave/minifi/minifi_flow_definition.py
diff --git a/behave_framework/src/minifi_test_framework/minifi/nifi_flow_definition.py b/behave_framework/src/minifi_behave/minifi/nifi_flow_definition.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/minifi/nifi_flow_definition.py
rename to behave_framework/src/minifi_behave/minifi/nifi_flow_definition.py
diff --git a/behave_framework/src/minifi_test_framework/minifi/output_port.py b/behave_framework/src/minifi_behave/minifi/output_port.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/minifi/output_port.py
rename to behave_framework/src/minifi_behave/minifi/output_port.py
diff --git a/behave_framework/src/minifi_test_framework/minifi/parameter.py b/behave_framework/src/minifi_behave/minifi/parameter.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/minifi/parameter.py
rename to behave_framework/src/minifi_behave/minifi/parameter.py
diff --git a/behave_framework/src/minifi_test_framework/minifi/parameter_context.py b/behave_framework/src/minifi_behave/minifi/parameter_context.py
similarity index 95%
rename from behave_framework/src/minifi_test_framework/minifi/parameter_context.py
rename to behave_framework/src/minifi_behave/minifi/parameter_context.py
index 201aec7bc..58cc9161e 100644
--- a/behave_framework/src/minifi_test_framework/minifi/parameter_context.py
+++ b/behave_framework/src/minifi_behave/minifi/parameter_context.py
@@ -17,7 +17,7 @@
 
 import uuid
 
-from minifi_test_framework.minifi.parameter import Parameter
+from minifi_behave.minifi.parameter import Parameter
 
 
 class ParameterContext:
diff --git a/behave_framework/src/minifi_test_framework/minifi/processor.py b/behave_framework/src/minifi_behave/minifi/processor.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/minifi/processor.py
rename to behave_framework/src/minifi_behave/minifi/processor.py
diff --git a/behave_framework/src/minifi_test_framework/minifi/remote_port.py b/behave_framework/src/minifi_behave/minifi/remote_port.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/minifi/remote_port.py
rename to behave_framework/src/minifi_behave/minifi/remote_port.py
diff --git a/behave_framework/src/minifi_test_framework/minifi/remote_process_group.py b/behave_framework/src/minifi_behave/minifi/remote_process_group.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/minifi/remote_process_group.py
rename to behave_framework/src/minifi_behave/minifi/remote_process_group.py
diff --git a/behave_framework/src/minifi_test_framework/steps/__init__.py b/behave_framework/src/minifi_behave/steps/__init__.py
similarity index 100%
rename from behave_framework/src/minifi_test_framework/steps/__init__.py
rename to behave_framework/src/minifi_behave/steps/__init__.py
diff --git a/behave_framework/src/minifi_test_framework/steps/checking_steps.py b/behave_framework/src/minifi_behave/steps/checking_steps.py
similarity index 98%
rename from behave_framework/src/minifi_test_framework/steps/checking_steps.py
rename to behave_framework/src/minifi_behave/steps/checking_steps.py
index a05a50aca..5f52ee740 100644
--- a/behave_framework/src/minifi_test_framework/steps/checking_steps.py
+++ b/behave_framework/src/minifi_behave/steps/checking_steps.py
@@ -20,9 +20,9 @@ import re
 import humanfriendly
 from behave import then, step
 
-from minifi_test_framework.containers.http_proxy_container import HttpProxy
-from minifi_test_framework.core.helpers import wait_for_condition, check_condition_after_wait, log_due_to_failure
-from minifi_test_framework.core.minifi_test_context import DEFAULT_MINIFI_CONTAINER_NAME, MinifiTestContext
+from minifi_behave.containers.http_proxy_container import HttpProxy
+from minifi_behave.core.helpers import wait_for_condition, check_condition_after_wait, log_due_to_failure
+from minifi_behave.core.minifi_test_context import DEFAULT_MINIFI_CONTAINER_NAME, MinifiTestContext
 
 
 @then('a file with the content "{content}" is placed on the path "{path}" in less than {duration}')
diff --git a/behave_framework/src/minifi_test_framework/steps/configuration_steps.py b/behave_framework/src/minifi_behave/steps/configuration_steps.py
similarity index 83%
rename from behave_framework/src/minifi_test_framework/steps/configuration_steps.py
rename to behave_framework/src/minifi_behave/steps/configuration_steps.py
index 7c2dd715c..605879b08 100644
--- a/behave_framework/src/minifi_test_framework/steps/configuration_steps.py
+++ b/behave_framework/src/minifi_behave/steps/configuration_steps.py
@@ -17,11 +17,11 @@
 
 from behave import step, given
 
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.containers.minifi_protocol import enable_openssl_fips_mode
-from minifi_test_framework.containers.minifi_protocol import set_up_ssl_properties
-from minifi_test_framework.containers.minifi_protocol import enable_log_metrics_publisher
-from minifi_test_framework.containers.minifi_protocol import configure_c2_flow_url
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.minifi_protocol import enable_openssl_fips_mode
+from minifi_behave.containers.minifi_protocol import set_up_ssl_properties
+from minifi_behave.containers.minifi_protocol import enable_log_metrics_publisher
+from minifi_behave.containers.minifi_protocol import configure_c2_flow_url
 
 
 @step('MiNiFi configuration "{config_key}" is set to "{config_value}"')
diff --git a/behave_framework/src/minifi_test_framework/steps/core_steps.py b/behave_framework/src/minifi_behave/steps/core_steps.py
similarity index 95%
rename from behave_framework/src/minifi_test_framework/steps/core_steps.py
rename to behave_framework/src/minifi_behave/steps/core_steps.py
index 70edd47c1..f2efef59b 100644
--- a/behave_framework/src/minifi_test_framework/steps/core_steps.py
+++ b/behave_framework/src/minifi_behave/steps/core_steps.py
@@ -25,11 +25,11 @@ import uuid
 import humanfriendly
 from behave import when, step, given
 
-from minifi_test_framework.containers.http_proxy_container import HttpProxy
-from minifi_test_framework.containers.nifi_container import NifiContainer
-from minifi_test_framework.containers.directory import Directory
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.core.minifi_test_context import DEFAULT_MINIFI_CONTAINER_NAME, MinifiTestContext
+from minifi_behave.containers.http_proxy_container import HttpProxy
+from minifi_behave.containers.nifi_container import NifiContainer
+from minifi_behave.containers.directory import Directory
+from minifi_behave.containers.file import File
+from minifi_behave.core.minifi_test_context import DEFAULT_MINIFI_CONTAINER_NAME, MinifiTestContext
 
 
 @when("both instances start up")
diff --git a/behave_framework/src/minifi_test_framework/steps/flow_building_steps.py b/behave_framework/src/minifi_behave/steps/flow_building_steps.py
similarity index 97%
rename from behave_framework/src/minifi_test_framework/steps/flow_building_steps.py
rename to behave_framework/src/minifi_behave/steps/flow_building_steps.py
index 94ef4613e..9d2a32b07 100644
--- a/behave_framework/src/minifi_test_framework/steps/flow_building_steps.py
+++ b/behave_framework/src/minifi_behave/steps/flow_building_steps.py
@@ -19,13 +19,13 @@ import logging
 import uuid
 from behave import given, step
 
-from minifi_test_framework.core.minifi_test_context import DEFAULT_MINIFI_CONTAINER_NAME, MinifiTestContext
-from minifi_test_framework.minifi.connection import Connection
-from minifi_test_framework.minifi.controller_service import ControllerService
-from minifi_test_framework.minifi.funnel import Funnel
-from minifi_test_framework.minifi.parameter import Parameter
-from minifi_test_framework.minifi.parameter_context import ParameterContext
-from minifi_test_framework.minifi.processor import Processor
+from minifi_behave.core.minifi_test_context import DEFAULT_MINIFI_CONTAINER_NAME, MinifiTestContext
+from minifi_behave.minifi.connection import Connection
+from minifi_behave.minifi.controller_service import ControllerService
+from minifi_behave.minifi.funnel import Funnel
+from minifi_behave.minifi.parameter import Parameter
+from minifi_behave.minifi.parameter_context import ParameterContext
+from minifi_behave.minifi.processor import Processor
 
 
 @given("a MiNiFi CPP server with yaml config")
diff --git a/extensions/aws/tests/features/containers/kinesis_server_container.py b/extensions/aws/tests/features/containers/kinesis_server_container.py
index d97bb5c83..f7767773c 100644
--- a/extensions/aws/tests/features/containers/kinesis_server_container.py
+++ b/extensions/aws/tests/features/containers/kinesis_server_container.py
@@ -16,10 +16,10 @@
 import logging
 
 from pathlib import Path
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.core.helpers import wait_for_condition, retry_check
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.core.helpers import wait_for_condition, retry_check
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
 
 
 class KinesisServerContainer(LinuxContainer):
diff --git a/extensions/aws/tests/features/containers/s3_server_container.py b/extensions/aws/tests/features/containers/s3_server_container.py
index 2364b2d0c..9a16700df 100644
--- a/extensions/aws/tests/features/containers/s3_server_container.py
+++ b/extensions/aws/tests/features/containers/s3_server_container.py
@@ -16,9 +16,9 @@
 import json
 import logging
 
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class S3ServerContainer(LinuxContainer):
diff --git a/extensions/aws/tests/features/environment.py b/extensions/aws/tests/features/environment.py
index 225347536..ee812b362 100644
--- a/extensions/aws/tests/features/environment.py
+++ b/extensions/aws/tests/features/environment.py
@@ -14,8 +14,8 @@
 # limitations under the License.
 
 
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 # These hooks are executed by behave before and after each scenario
 # The common_before_scenario and common_after_scenario must be called for proper setup and tear down
diff --git a/extensions/aws/tests/features/steps/steps.py b/extensions/aws/tests/features/steps/steps.py
index 2d8d31c44..ab1094046 100644
--- a/extensions/aws/tests/features/steps/steps.py
+++ b/extensions/aws/tests/features/steps/steps.py
@@ -20,14 +20,14 @@ import string
 import humanfriendly
 from behave import step, then
 
-from minifi_test_framework.containers.directory import Directory
-from minifi_test_framework.steps import checking_steps        # noqa: F401
-from minifi_test_framework.steps import configuration_steps   # noqa: F401
-from minifi_test_framework.steps import core_steps            # noqa: F401
-from minifi_test_framework.steps import flow_building_steps   # noqa: F401
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.minifi.processor import Processor
-from minifi_test_framework.core.helpers import wait_for_condition, log_due_to_failure
+from minifi_behave.containers.directory import Directory
+from minifi_behave.steps import checking_steps        # noqa: F401
+from minifi_behave.steps import configuration_steps   # noqa: F401
+from minifi_behave.steps import core_steps            # noqa: F401
+from minifi_behave.steps import flow_building_steps   # noqa: F401
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.minifi.processor import Processor
+from minifi_behave.core.helpers import wait_for_condition, log_due_to_failure
 
 from containers.s3_server_container import S3ServerContainer
 from containers.kinesis_server_container import KinesisServerContainer
diff --git a/extensions/azure/tests/features/containers/azure_server_container.py b/extensions/azure/tests/features/containers/azure_server_container.py
index cb0d2e5eb..36f81158f 100644
--- a/extensions/azure/tests/features/containers/azure_server_container.py
+++ b/extensions/azure/tests/features/containers/azure_server_container.py
@@ -18,10 +18,10 @@
 import logging
 
 from docker.errors import ContainerError
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.core.helpers import run_cmd_in_docker_image
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.core.helpers import run_cmd_in_docker_image
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class AzureServerContainer(LinuxContainer):
diff --git a/extensions/azure/tests/features/environment.py b/extensions/azure/tests/features/environment.py
index 225347536..ee812b362 100644
--- a/extensions/azure/tests/features/environment.py
+++ b/extensions/azure/tests/features/environment.py
@@ -14,8 +14,8 @@
 # limitations under the License.
 
 
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 # These hooks are executed by behave before and after each scenario
 # The common_before_scenario and common_after_scenario must be called for proper setup and tear down
diff --git a/extensions/azure/tests/features/steps/steps.py b/extensions/azure/tests/features/steps/steps.py
index 35ea6b0d8..25409d427 100644
--- a/extensions/azure/tests/features/steps/steps.py
+++ b/extensions/azure/tests/features/steps/steps.py
@@ -17,13 +17,13 @@
 
 import humanfriendly
 from behave import step, then
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.minifi.processor import Processor
-from minifi_test_framework.steps import checking_steps  # noqa: F401
-from minifi_test_framework.steps import configuration_steps  # noqa: F401
-from minifi_test_framework.steps import core_steps  # noqa: F401
-from minifi_test_framework.steps import flow_building_steps  # noqa: F401
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.minifi.processor import Processor
+from minifi_behave.steps import checking_steps  # noqa: F401
+from minifi_behave.steps import configuration_steps  # noqa: F401
+from minifi_behave.steps import core_steps  # noqa: F401
+from minifi_behave.steps import flow_building_steps  # noqa: F401
 
 from containers.azure_server_container import AzureServerContainer
 
diff --git a/extensions/civetweb/tests/features/environment.py b/extensions/civetweb/tests/features/environment.py
index d6c856340..5b3659f58 100644
--- a/extensions/civetweb/tests/features/environment.py
+++ b/extensions/civetweb/tests/features/environment.py
@@ -13,8 +13,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 
 def before_scenario(context, scenario):
diff --git a/extensions/civetweb/tests/features/steps/steps.py b/extensions/civetweb/tests/features/steps/steps.py
index 90304a7f8..25759880f 100644
--- a/extensions/civetweb/tests/features/steps/steps.py
+++ b/extensions/civetweb/tests/features/steps/steps.py
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from minifi_test_framework.steps import checking_steps  # noqa: F401
-from minifi_test_framework.steps import configuration_steps  # noqa: F401
-from minifi_test_framework.steps import core_steps  # noqa: F401
-from minifi_test_framework.steps import flow_building_steps  # noqa: F401
+from minifi_behave.steps import checking_steps  # noqa: F401
+from minifi_behave.steps import configuration_steps  # noqa: F401
+from minifi_behave.steps import core_steps  # noqa: F401
+from minifi_behave.steps import flow_building_steps  # noqa: F401
diff --git a/extensions/couchbase/tests/features/containers/couchbase_server_container.py b/extensions/couchbase/tests/features/containers/couchbase_server_container.py
index 3f4f1401f..d8febe939 100644
--- a/extensions/couchbase/tests/features/containers/couchbase_server_container.py
+++ b/extensions/couchbase/tests/features/containers/couchbase_server_container.py
@@ -15,11 +15,11 @@
 
 import logging
 
-from minifi_test_framework.core.helpers import wait_for_condition, retry_check
-from minifi_test_framework.core.ssl_utils import make_server_cert, dump_cert, dump_key
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.helpers import wait_for_condition, retry_check
+from minifi_behave.core.ssl_utils import make_server_cert, dump_cert, dump_key
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.containers.file import File
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 from docker.errors import ContainerError
 
 
diff --git a/extensions/couchbase/tests/features/environment.py b/extensions/couchbase/tests/features/environment.py
index 01c4d4791..9b593f032 100644
--- a/extensions/couchbase/tests/features/environment.py
+++ b/extensions/couchbase/tests/features/environment.py
@@ -12,9 +12,9 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 
 def before_all(context):
diff --git a/extensions/couchbase/tests/features/steps/steps.py b/extensions/couchbase/tests/features/steps/steps.py
index 7a9972a92..9f0edeffc 100644
--- a/extensions/couchbase/tests/features/steps/steps.py
+++ b/extensions/couchbase/tests/features/steps/steps.py
@@ -14,14 +14,14 @@
 # limitations under the License.
 from behave import step, then
 
-from minifi_test_framework.steps import checking_steps        # noqa: F401
-from minifi_test_framework.steps import configuration_steps   # noqa: F401
-from minifi_test_framework.steps import core_steps            # noqa: F401
-from minifi_test_framework.steps import flow_building_steps   # noqa: F401
-from minifi_test_framework.steps.flow_building_steps import add_ssl_context_service_for_minifi
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.core.helpers import log_due_to_failure
-from minifi_test_framework.minifi.controller_service import ControllerService
+from minifi_behave.steps import checking_steps        # noqa: F401
+from minifi_behave.steps import configuration_steps   # noqa: F401
+from minifi_behave.steps import core_steps            # noqa: F401
+from minifi_behave.steps import flow_building_steps   # noqa: F401
+from minifi_behave.steps.flow_building_steps import add_ssl_context_service_for_minifi
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.helpers import log_due_to_failure
+from minifi_behave.minifi.controller_service import ControllerService
 from containers.couchbase_server_container import CouchbaseServerContainer
 
 
diff --git a/extensions/elasticsearch/tests/features/containers/elastic_base_container.py b/extensions/elasticsearch/tests/features/containers/elastic_base_container.py
index 2287278c4..577160c13 100644
--- a/extensions/elasticsearch/tests/features/containers/elastic_base_container.py
+++ b/extensions/elasticsearch/tests/features/containers/elastic_base_container.py
@@ -13,9 +13,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from minifi_test_framework.core.helpers import wait_for_condition, retry_check
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.helpers import wait_for_condition, retry_check
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class ElasticBaseContainer(LinuxContainer):
diff --git a/extensions/elasticsearch/tests/features/containers/elasticsearch_container.py b/extensions/elasticsearch/tests/features/containers/elasticsearch_container.py
index aec928026..3d87dd0a2 100644
--- a/extensions/elasticsearch/tests/features/containers/elasticsearch_container.py
+++ b/extensions/elasticsearch/tests/features/containers/elasticsearch_container.py
@@ -19,10 +19,10 @@ import os
 
 from elastic_base_container import ElasticBaseContainer
 from pathlib import Path
-from minifi_test_framework.core.ssl_utils import make_server_cert, make_cert_without_extended_usage, dump_cert, dump_key
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.containers.host_file import HostFile
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.ssl_utils import make_server_cert, make_cert_without_extended_usage, dump_cert, dump_key
+from minifi_behave.containers.file import File
+from minifi_behave.containers.host_file import HostFile
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class ElasticsearchContainer(ElasticBaseContainer):
diff --git a/extensions/elasticsearch/tests/features/containers/opensearch_container.py b/extensions/elasticsearch/tests/features/containers/opensearch_container.py
index 234a26d12..964e989c0 100644
--- a/extensions/elasticsearch/tests/features/containers/opensearch_container.py
+++ b/extensions/elasticsearch/tests/features/containers/opensearch_container.py
@@ -18,10 +18,10 @@ import logging
 
 from elastic_base_container import ElasticBaseContainer
 from pathlib import Path
-from minifi_test_framework.core.ssl_utils import make_server_cert, dump_cert, dump_key
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.containers.host_file import HostFile
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.ssl_utils import make_server_cert, dump_cert, dump_key
+from minifi_behave.containers.file import File
+from minifi_behave.containers.host_file import HostFile
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class OpensearchContainer(ElasticBaseContainer):
diff --git a/extensions/elasticsearch/tests/features/environment.py b/extensions/elasticsearch/tests/features/environment.py
index ab35b52a5..d9f4bcb30 100644
--- a/extensions/elasticsearch/tests/features/environment.py
+++ b/extensions/elasticsearch/tests/features/environment.py
@@ -12,8 +12,8 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 
 def before_scenario(context, scenario):
diff --git a/extensions/elasticsearch/tests/features/steps/steps.py b/extensions/elasticsearch/tests/features/steps/steps.py
index 7059c1cd4..966c7a3b1 100644
--- a/extensions/elasticsearch/tests/features/steps/steps.py
+++ b/extensions/elasticsearch/tests/features/steps/steps.py
@@ -14,13 +14,13 @@
 # limitations under the License.
 from behave import step, given, then
 
-from minifi_test_framework.steps import checking_steps        # noqa: F401
-from minifi_test_framework.steps import configuration_steps   # noqa: F401
-from minifi_test_framework.steps import core_steps            # noqa: F401
-from minifi_test_framework.steps import flow_building_steps   # noqa: F401
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.minifi.controller_service import ControllerService
-from minifi_test_framework.core.helpers import log_due_to_failure
+from minifi_behave.steps import checking_steps        # noqa: F401
+from minifi_behave.steps import configuration_steps   # noqa: F401
+from minifi_behave.steps import core_steps            # noqa: F401
+from minifi_behave.steps import flow_building_steps   # noqa: F401
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.minifi.controller_service import ControllerService
+from minifi_behave.core.helpers import log_due_to_failure
 from containers.elasticsearch_container import ElasticsearchContainer
 from containers.opensearch_container import OpensearchContainer
 
diff --git a/extensions/gcp/tests/features/containers/fake_gcs_server_container.py b/extensions/gcp/tests/features/containers/fake_gcs_server_container.py
index 5689065bb..2bb63fbf9 100644
--- a/extensions/gcp/tests/features/containers/fake_gcs_server_container.py
+++ b/extensions/gcp/tests/features/containers/fake_gcs_server_container.py
@@ -14,10 +14,10 @@
 # limitations under the License.
 
 import logging
-from minifi_test_framework.core.helpers import wait_for_condition, retry_check
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.containers.directory import Directory
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.helpers import wait_for_condition, retry_check
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.containers.directory import Directory
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class FakeGcsServerContainer(LinuxContainer):
diff --git a/extensions/gcp/tests/features/environment.py b/extensions/gcp/tests/features/environment.py
index ab35b52a5..d9f4bcb30 100644
--- a/extensions/gcp/tests/features/environment.py
+++ b/extensions/gcp/tests/features/environment.py
@@ -12,8 +12,8 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 
 def before_scenario(context, scenario):
diff --git a/extensions/gcp/tests/features/steps/steps.py b/extensions/gcp/tests/features/steps/steps.py
index 57798562d..b2e9308ec 100644
--- a/extensions/gcp/tests/features/steps/steps.py
+++ b/extensions/gcp/tests/features/steps/steps.py
@@ -14,12 +14,12 @@
 # limitations under the License.
 from behave import step, then
 
-from minifi_test_framework.steps import checking_steps        # noqa: F401
-from minifi_test_framework.steps import configuration_steps   # noqa: F401
-from minifi_test_framework.steps import core_steps            # noqa: F401
-from minifi_test_framework.steps import flow_building_steps   # noqa: F401
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.core.helpers import log_due_to_failure
+from minifi_behave.steps import checking_steps        # noqa: F401
+from minifi_behave.steps import configuration_steps   # noqa: F401
+from minifi_behave.steps import core_steps            # noqa: F401
+from minifi_behave.steps import flow_building_steps   # noqa: F401
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.helpers import log_due_to_failure
 from containers.fake_gcs_server_container import FakeGcsServerContainer
 
 
diff --git a/extensions/grafana-loki/tests/features/containers/grafana_loki_container.py b/extensions/grafana-loki/tests/features/containers/grafana_loki_container.py
index af296c597..9e907e855 100644
--- a/extensions/grafana-loki/tests/features/containers/grafana_loki_container.py
+++ b/extensions/grafana-loki/tests/features/containers/grafana_loki_container.py
@@ -15,11 +15,11 @@
 
 import logging
 
-from minifi_test_framework.core.helpers import wait_for_condition, retry_check
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.core.ssl_utils import make_server_cert, dump_cert, dump_key
+from minifi_behave.core.helpers import wait_for_condition, retry_check
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.containers.file import File
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.ssl_utils import make_server_cert, dump_cert, dump_key
 from docker.errors import ContainerError
 
 
diff --git a/extensions/grafana-loki/tests/features/containers/reverse_proxy_container.py b/extensions/grafana-loki/tests/features/containers/reverse_proxy_container.py
index 43efd49c5..f622cbda3 100644
--- a/extensions/grafana-loki/tests/features/containers/reverse_proxy_container.py
+++ b/extensions/grafana-loki/tests/features/containers/reverse_proxy_container.py
@@ -13,9 +13,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.core.helpers import wait_for_condition
 
 
 class ReverseProxyContainer(LinuxContainer):
diff --git a/extensions/grafana-loki/tests/features/environment.py b/extensions/grafana-loki/tests/features/environment.py
index 11ab6ba74..4e25390ed 100644
--- a/extensions/grafana-loki/tests/features/environment.py
+++ b/extensions/grafana-loki/tests/features/environment.py
@@ -13,9 +13,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 from pathlib import Path
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 
 def before_all(context):
diff --git a/extensions/grafana-loki/tests/features/steps/steps.py b/extensions/grafana-loki/tests/features/steps/steps.py
index eb34ac28f..0ab076923 100644
--- a/extensions/grafana-loki/tests/features/steps/steps.py
+++ b/extensions/grafana-loki/tests/features/steps/steps.py
@@ -14,12 +14,12 @@
 # limitations under the License.
 from behave import step, then
 
-from minifi_test_framework.steps import checking_steps        # noqa: F401
-from minifi_test_framework.steps import configuration_steps   # noqa: F401
-from minifi_test_framework.steps import core_steps            # noqa: F401
-from minifi_test_framework.steps import flow_building_steps   # noqa: F401
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.core.helpers import log_due_to_failure
+from minifi_behave.steps import checking_steps        # noqa: F401
+from minifi_behave.steps import configuration_steps   # noqa: F401
+from minifi_behave.steps import core_steps            # noqa: F401
+from minifi_behave.steps import flow_building_steps   # noqa: F401
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.helpers import log_due_to_failure
 from containers.grafana_loki_container import GrafanaLokiContainer, GrafanaLokiOptions
 from containers.reverse_proxy_container import ReverseProxyContainer
 
diff --git a/extensions/kafka/tests/features/containers/kafka_server_container.py b/extensions/kafka/tests/features/containers/kafka_server_container.py
index 09e35e131..350a05478 100644
--- a/extensions/kafka/tests/features/containers/kafka_server_container.py
+++ b/extensions/kafka/tests/features/containers/kafka_server_container.py
@@ -19,11 +19,11 @@ import re
 from cryptography.hazmat.primitives import serialization
 import jks
 
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.ssl_utils import make_server_cert, dump_cert, dump_key
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.ssl_utils import make_server_cert, dump_cert, dump_key
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.containers.file import File
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class KafkaServer(LinuxContainer):
diff --git a/extensions/kafka/tests/features/environment.py b/extensions/kafka/tests/features/environment.py
index 99986cd33..4d9f7006b 100644
--- a/extensions/kafka/tests/features/environment.py
+++ b/extensions/kafka/tests/features/environment.py
@@ -12,9 +12,9 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 
 def before_all(context):
diff --git a/extensions/kafka/tests/features/steps/steps.py b/extensions/kafka/tests/features/steps/steps.py
index 05b2c3f57..7417e709f 100644
--- a/extensions/kafka/tests/features/steps/steps.py
+++ b/extensions/kafka/tests/features/steps/steps.py
@@ -18,12 +18,12 @@ import time
 
 from behave import step, when, given
 
-from minifi_test_framework.steps import checking_steps        # noqa: F401
-from minifi_test_framework.steps import configuration_steps   # noqa: F401
-from minifi_test_framework.steps import core_steps            # noqa: F401
-from minifi_test_framework.steps import flow_building_steps   # noqa: F401
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.minifi.processor import Processor
+from minifi_behave.steps import checking_steps        # noqa: F401
+from minifi_behave.steps import configuration_steps   # noqa: F401
+from minifi_behave.steps import core_steps            # noqa: F401
+from minifi_behave.steps import flow_building_steps   # noqa: F401
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.minifi.processor import Processor
 from containers.kafka_server_container import KafkaServer
 
 
diff --git a/extensions/kubernetes/tests/features/environment.py b/extensions/kubernetes/tests/features/environment.py
index f25e1dd52..83408016a 100644
--- a/extensions/kubernetes/tests/features/environment.py
+++ b/extensions/kubernetes/tests/features/environment.py
@@ -16,7 +16,7 @@
 import docker
 import os
 
-from minifi_test_framework.core.hooks import common_after_scenario, common_before_scenario, get_minifi_container_image
+from minifi_behave.core.hooks import common_after_scenario, common_before_scenario, get_minifi_container_image
 from steps.kubernetes_proxy import KubernetesProxy
 
 
diff --git a/extensions/kubernetes/tests/features/steps/minifi_as_pod_in_kubernetes_cluster.py b/extensions/kubernetes/tests/features/steps/minifi_as_pod_in_kubernetes_cluster.py
index b741b185d..320e89686 100644
--- a/extensions/kubernetes/tests/features/steps/minifi_as_pod_in_kubernetes_cluster.py
+++ b/extensions/kubernetes/tests/features/steps/minifi_as_pod_in_kubernetes_cluster.py
@@ -15,8 +15,8 @@
 
 import logging
 
-from minifi_test_framework.containers.minifi_container import MinifiContainer
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.minifi_container import MinifiContainer
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class MinifiAsPodInKubernetesCluster(MinifiContainer):
diff --git a/extensions/kubernetes/tests/features/steps/steps.py b/extensions/kubernetes/tests/features/steps/steps.py
index 7e5c2f2ad..b153cadb0 100644
--- a/extensions/kubernetes/tests/features/steps/steps.py
+++ b/extensions/kubernetes/tests/features/steps/steps.py
@@ -15,14 +15,14 @@
 
 from behave import given
 
-from minifi_test_framework.steps import checking_steps  # noqa: F401
-from minifi_test_framework.steps import configuration_steps  # noqa: F401
-from minifi_test_framework.steps import core_steps  # noqa: F401
-from minifi_test_framework.steps import flow_building_steps  # noqa: F401
-
-from minifi_test_framework.core.minifi_test_context import DEFAULT_MINIFI_CONTAINER_NAME, MinifiTestContext
-from minifi_test_framework.minifi.processor import Processor
-from minifi_test_framework.minifi.controller_service import ControllerService
+from minifi_behave.steps import checking_steps  # noqa: F401
+from minifi_behave.steps import configuration_steps  # noqa: F401
+from minifi_behave.steps import core_steps  # noqa: F401
+from minifi_behave.steps import flow_building_steps  # noqa: F401
+
+from minifi_behave.core.minifi_test_context import DEFAULT_MINIFI_CONTAINER_NAME, MinifiTestContext
+from minifi_behave.minifi.processor import Processor
+from minifi_behave.minifi.controller_service import ControllerService
 from minifi_as_pod_in_kubernetes_cluster import MinifiAsPodInKubernetesCluster
 
 
diff --git a/extensions/llamacpp/tests/features/environment.py b/extensions/llamacpp/tests/features/environment.py
index 98caaa7d0..9f23b7656 100644
--- a/extensions/llamacpp/tests/features/environment.py
+++ b/extensions/llamacpp/tests/features/environment.py
@@ -14,11 +14,11 @@
 # limitations under the License.
 from textwrap import dedent
 
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
-from minifi_test_framework.core.hooks import get_minifi_container_image
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
+from minifi_behave.core.hooks import get_minifi_container_image
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 # These hooks are executed by behave before and after each scenario
 # The common_before_scenario and common_after_scenario must be called for proper setup and tear down
diff --git a/extensions/llamacpp/tests/features/steps/steps.py b/extensions/llamacpp/tests/features/steps/steps.py
index 90304a7f8..25759880f 100644
--- a/extensions/llamacpp/tests/features/steps/steps.py
+++ b/extensions/llamacpp/tests/features/steps/steps.py
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from minifi_test_framework.steps import checking_steps  # noqa: F401
-from minifi_test_framework.steps import configuration_steps  # noqa: F401
-from minifi_test_framework.steps import core_steps  # noqa: F401
-from minifi_test_framework.steps import flow_building_steps  # noqa: F401
+from minifi_behave.steps import checking_steps  # noqa: F401
+from minifi_behave.steps import configuration_steps  # noqa: F401
+from minifi_behave.steps import core_steps  # noqa: F401
+from minifi_behave.steps import flow_building_steps  # noqa: F401
diff --git a/extensions/lua/tests/features/environment.py b/extensions/lua/tests/features/environment.py
index a02cce4c3..7e9fcda28 100644
--- a/extensions/lua/tests/features/environment.py
+++ b/extensions/lua/tests/features/environment.py
@@ -15,8 +15,8 @@
 
 import os
 
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 
 def before_scenario(context, scenario):
diff --git a/extensions/lua/tests/features/steps/steps.py b/extensions/lua/tests/features/steps/steps.py
index 90304a7f8..25759880f 100644
--- a/extensions/lua/tests/features/steps/steps.py
+++ b/extensions/lua/tests/features/steps/steps.py
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from minifi_test_framework.steps import checking_steps  # noqa: F401
-from minifi_test_framework.steps import configuration_steps  # noqa: F401
-from minifi_test_framework.steps import core_steps  # noqa: F401
-from minifi_test_framework.steps import flow_building_steps  # noqa: F401
+from minifi_behave.steps import checking_steps  # noqa: F401
+from minifi_behave.steps import configuration_steps  # noqa: F401
+from minifi_behave.steps import core_steps  # noqa: F401
+from minifi_behave.steps import flow_building_steps  # noqa: F401
diff --git a/extensions/mqtt/tests/features/containers/mqtt_broker_container.py b/extensions/mqtt/tests/features/containers/mqtt_broker_container.py
index c4f4b7416..2ed54d1ba 100644
--- a/extensions/mqtt/tests/features/containers/mqtt_broker_container.py
+++ b/extensions/mqtt/tests/features/containers/mqtt_broker_container.py
@@ -17,10 +17,10 @@ import logging
 import re
 from textwrap import dedent
 
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
 from docker.errors import ContainerError
 
 
diff --git a/extensions/mqtt/tests/features/environment.py b/extensions/mqtt/tests/features/environment.py
index b3bbe9df0..a98f91b7d 100644
--- a/extensions/mqtt/tests/features/environment.py
+++ b/extensions/mqtt/tests/features/environment.py
@@ -14,9 +14,9 @@
 # limitations under the License.
 
 from pathlib import Path
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 
 def before_all(context):
diff --git a/extensions/mqtt/tests/features/steps/steps.py b/extensions/mqtt/tests/features/steps/steps.py
index e16b8014e..1bce835ae 100644
--- a/extensions/mqtt/tests/features/steps/steps.py
+++ b/extensions/mqtt/tests/features/steps/steps.py
@@ -16,13 +16,13 @@
 import re
 from behave import given, step, then, when
 
-from minifi_test_framework.steps import checking_steps        # noqa: F401
-from minifi_test_framework.steps import configuration_steps   # noqa: F401
-from minifi_test_framework.steps import core_steps            # noqa: F401
-from minifi_test_framework.steps import flow_building_steps   # noqa: F401
-from minifi_test_framework.core.minifi_test_context import DEFAULT_MINIFI_CONTAINER_NAME, MinifiTestContext
-from minifi_test_framework.minifi.processor import Processor
-from minifi_test_framework.core.helpers import wait_for_condition
+from minifi_behave.steps import checking_steps        # noqa: F401
+from minifi_behave.steps import configuration_steps   # noqa: F401
+from minifi_behave.steps import core_steps            # noqa: F401
+from minifi_behave.steps import flow_building_steps   # noqa: F401
+from minifi_behave.core.minifi_test_context import DEFAULT_MINIFI_CONTAINER_NAME, MinifiTestContext
+from minifi_behave.minifi.processor import Processor
+from minifi_behave.core.helpers import wait_for_condition
 
 from containers.mqtt_broker_container import MqttBrokerContainer
 
diff --git a/extensions/opc/tests/features/containers/opc_ua_server_container.py b/extensions/opc/tests/features/containers/opc_ua_server_container.py
index 3740b77bd..5894bc0cd 100644
--- a/extensions/opc/tests/features/containers/opc_ua_server_container.py
+++ b/extensions/opc/tests/features/containers/opc_ua_server_container.py
@@ -14,9 +14,9 @@
 # limitations under the License.
 
 from typing import List, Optional
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class OPCUAServerContainer(LinuxContainer):
diff --git a/extensions/opc/tests/features/environment.py b/extensions/opc/tests/features/environment.py
index a02cce4c3..7e9fcda28 100644
--- a/extensions/opc/tests/features/environment.py
+++ b/extensions/opc/tests/features/environment.py
@@ -15,8 +15,8 @@
 
 import os
 
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 
 def before_scenario(context, scenario):
diff --git a/extensions/opc/tests/features/steps/steps.py b/extensions/opc/tests/features/steps/steps.py
index 2c17dbc6a..0dcd61c89 100644
--- a/extensions/opc/tests/features/steps/steps.py
+++ b/extensions/opc/tests/features/steps/steps.py
@@ -22,12 +22,12 @@ import tarfile
 import humanfriendly
 
 from behave import given, step, then
-from minifi_test_framework.steps import checking_steps        # noqa: F401
-from minifi_test_framework.steps import configuration_steps   # noqa: F401
-from minifi_test_framework.steps import core_steps            # noqa: F401
-from minifi_test_framework.steps import flow_building_steps   # noqa: F401
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.core.helpers import wait_for_condition
+from minifi_behave.steps import checking_steps        # noqa: F401
+from minifi_behave.steps import configuration_steps   # noqa: F401
+from minifi_behave.steps import core_steps            # noqa: F401
+from minifi_behave.steps import flow_building_steps   # noqa: F401
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.helpers import wait_for_condition
 from containers.opc_ua_server_container import OPCUAServerContainer
 
 
diff --git a/extensions/prometheus/tests/features/containers/prometheus_container.py b/extensions/prometheus/tests/features/containers/prometheus_container.py
index 829bd892e..4846d9307 100644
--- a/extensions/prometheus/tests/features/containers/prometheus_container.py
+++ b/extensions/prometheus/tests/features/containers/prometheus_container.py
@@ -14,12 +14,12 @@
 # limitations under the License.
 
 import logging
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.core.ssl_utils import make_cert_without_extended_usage
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.core.ssl_utils import dump_cert, dump_key
-from minifi_test_framework.containers.container_linux import LinuxContainer
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.ssl_utils import make_cert_without_extended_usage
+from minifi_behave.containers.file import File
+from minifi_behave.core.ssl_utils import dump_cert, dump_key
+from minifi_behave.containers.container_linux import LinuxContainer
 
 
 class PrometheusContainer(LinuxContainer):
diff --git a/extensions/prometheus/tests/features/environment.py b/extensions/prometheus/tests/features/environment.py
index 6b7a9bbc0..df890c1fc 100644
--- a/extensions/prometheus/tests/features/environment.py
+++ b/extensions/prometheus/tests/features/environment.py
@@ -13,9 +13,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 from pathlib import Path
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 
 def before_all(context):
diff --git a/extensions/prometheus/tests/features/steps/steps.py b/extensions/prometheus/tests/features/steps/steps.py
index 1a181def4..eb27aecb7 100644
--- a/extensions/prometheus/tests/features/steps/steps.py
+++ b/extensions/prometheus/tests/features/steps/steps.py
@@ -14,12 +14,12 @@
 # limitations under the License.
 from behave import step, then, given
 
-from minifi_test_framework.steps import checking_steps        # noqa: F401
-from minifi_test_framework.steps import configuration_steps   # noqa: F401
-from minifi_test_framework.steps import core_steps            # noqa: F401
-from minifi_test_framework.steps import flow_building_steps   # noqa: F401
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.steps import checking_steps        # noqa: F401
+from minifi_behave.steps import configuration_steps   # noqa: F401
+from minifi_behave.steps import core_steps            # noqa: F401
+from minifi_behave.steps import flow_building_steps   # noqa: F401
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 from containers.prometheus_container import PrometheusContainer
 
 
diff --git a/extensions/python/tests/features/environment.py b/extensions/python/tests/features/environment.py
index d22adcf27..abdf1b69b 100644
--- a/extensions/python/tests/features/environment.py
+++ b/extensions/python/tests/features/environment.py
@@ -15,9 +15,9 @@
 import docker
 import os
 from pathlib import Path
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 
 def get_minifi_container_image():
diff --git a/extensions/python/tests/features/steps/steps.py b/extensions/python/tests/features/steps/steps.py
index 7180f522f..6b0dc4a47 100644
--- a/extensions/python/tests/features/steps/steps.py
+++ b/extensions/python/tests/features/steps/steps.py
@@ -16,12 +16,12 @@ import os
 import docker
 from behave import given
 
-from minifi_test_framework.steps import checking_steps        # noqa: F401
-from minifi_test_framework.steps import configuration_steps   # noqa: F401
-from minifi_test_framework.steps import core_steps            # noqa: F401
-from minifi_test_framework.steps import flow_building_steps   # noqa: F401
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.steps import checking_steps        # noqa: F401
+from minifi_behave.steps import configuration_steps   # noqa: F401
+from minifi_behave.steps import core_steps            # noqa: F401
+from minifi_behave.steps import flow_building_steps   # noqa: F401
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
 
 
 class PythonWithDependenciesOptions:
diff --git a/extensions/splunk/tests/features/containers/splunk_container.py b/extensions/splunk/tests/features/containers/splunk_container.py
index 9a1488e63..8eb87b541 100644
--- a/extensions/splunk/tests/features/containers/splunk_container.py
+++ b/extensions/splunk/tests/features/containers/splunk_container.py
@@ -15,11 +15,11 @@
 
 import json
 
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.core.helpers import wait_for_condition, retry_check
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.core.ssl_utils import make_server_cert, dump_cert, dump_key
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.helpers import wait_for_condition, retry_check
+from minifi_behave.containers.file import File
+from minifi_behave.core.ssl_utils import make_server_cert, dump_cert, dump_key
 
 
 class SplunkContainer(LinuxContainer):
diff --git a/extensions/splunk/tests/features/environment.py b/extensions/splunk/tests/features/environment.py
index 347b35688..ca4d81cfb 100644
--- a/extensions/splunk/tests/features/environment.py
+++ b/extensions/splunk/tests/features/environment.py
@@ -15,8 +15,8 @@
 
 import platform
 
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 
 def before_feature(context, feature):
diff --git a/extensions/splunk/tests/features/steps/steps.py b/extensions/splunk/tests/features/steps/steps.py
index 1656142f2..c89a32955 100644
--- a/extensions/splunk/tests/features/steps/steps.py
+++ b/extensions/splunk/tests/features/steps/steps.py
@@ -14,12 +14,12 @@
 # limitations under the License.
 from behave import step, then
 
-from minifi_test_framework.steps import checking_steps        # noqa: F401
-from minifi_test_framework.steps import configuration_steps   # noqa: F401
-from minifi_test_framework.steps import core_steps            # noqa: F401
-from minifi_test_framework.steps import flow_building_steps   # noqa: F401
-from minifi_test_framework.core.helpers import log_due_to_failure
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.steps import checking_steps        # noqa: F401
+from minifi_behave.steps import configuration_steps   # noqa: F401
+from minifi_behave.steps import core_steps            # noqa: F401
+from minifi_behave.steps import flow_building_steps   # noqa: F401
+from minifi_behave.core.helpers import log_due_to_failure
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 from containers.splunk_container import SplunkContainer
 
 
diff --git a/extensions/sql/tests/features/containers/postgress_server_container.py b/extensions/sql/tests/features/containers/postgress_server_container.py
index 2dea3058e..b84aef130 100644
--- a/extensions/sql/tests/features/containers/postgress_server_container.py
+++ b/extensions/sql/tests/features/containers/postgress_server_container.py
@@ -17,10 +17,10 @@
 
 import logging
 from textwrap import dedent
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class PostgresContainer(LinuxContainer):
diff --git a/extensions/sql/tests/features/environment.py b/extensions/sql/tests/features/environment.py
index a835e3b2c..ad369afc0 100644
--- a/extensions/sql/tests/features/environment.py
+++ b/extensions/sql/tests/features/environment.py
@@ -18,10 +18,10 @@
 import os
 from textwrap import dedent
 
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
-from minifi_test_framework.core.hooks import get_minifi_container_image
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
+from minifi_behave.core.hooks import get_minifi_container_image
 
 # These hooks are executed by behave before and after each scenario
 # The common_before_scenario and common_after_scenario must be called for proper setup and tear down
diff --git a/extensions/sql/tests/features/steps/steps.py b/extensions/sql/tests/features/steps/steps.py
index eb3c90ce7..92b95d22b 100644
--- a/extensions/sql/tests/features/steps/steps.py
+++ b/extensions/sql/tests/features/steps/steps.py
@@ -18,13 +18,13 @@
 import humanfriendly
 from behave import step, given, then
 
-from minifi_test_framework.steps import checking_steps        # noqa: F401
-from minifi_test_framework.steps import configuration_steps   # noqa: F401
-from minifi_test_framework.steps import core_steps            # noqa: F401
-from minifi_test_framework.steps import flow_building_steps   # noqa: F401
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.minifi.controller_service import ControllerService
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.steps import checking_steps        # noqa: F401
+from minifi_behave.steps import configuration_steps   # noqa: F401
+from minifi_behave.steps import core_steps            # noqa: F401
+from minifi_behave.steps import flow_building_steps   # noqa: F401
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.minifi.controller_service import ControllerService
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 from containers.postgress_server_container import PostgresContainer
 
 
diff --git a/extensions/standard-processors/tests/features/containers/diag_slave_container.py b/extensions/standard-processors/tests/features/containers/diag_slave_container.py
index 4741441c4..68f78ae64 100644
--- a/extensions/standard-processors/tests/features/containers/diag_slave_container.py
+++ b/extensions/standard-processors/tests/features/containers/diag_slave_container.py
@@ -18,10 +18,10 @@
 import logging
 from textwrap import dedent
 
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.containers.docker_image_builder import DockerImageBuilder
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class DiagSlave(LinuxContainer):
diff --git a/extensions/standard-processors/tests/features/containers/syslog_container.py b/extensions/standard-processors/tests/features/containers/syslog_container.py
index c831af6f1..8cbb117d1 100644
--- a/extensions/standard-processors/tests/features/containers/syslog_container.py
+++ b/extensions/standard-processors/tests/features/containers/syslog_container.py
@@ -15,7 +15,7 @@
 #  limitations under the License.
 #
 
-from minifi_test_framework.containers.container_linux import LinuxContainer
+from minifi_behave.containers.container_linux import LinuxContainer
 
 
 class SyslogContainer(LinuxContainer):
diff --git a/extensions/standard-processors/tests/features/containers/tcp_client_container.py b/extensions/standard-processors/tests/features/containers/tcp_client_container.py
index 74fcf1351..37b8cbb31 100644
--- a/extensions/standard-processors/tests/features/containers/tcp_client_container.py
+++ b/extensions/standard-processors/tests/features/containers/tcp_client_container.py
@@ -13,9 +13,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 class TcpClientContainer(LinuxContainer):
diff --git a/extensions/standard-processors/tests/features/environment.py b/extensions/standard-processors/tests/features/environment.py
index a2c3531ce..fb2b3b72c 100644
--- a/extensions/standard-processors/tests/features/environment.py
+++ b/extensions/standard-processors/tests/features/environment.py
@@ -16,8 +16,8 @@ import os
 import platform
 
 import docker
-from minifi_test_framework.core.hooks import common_before_scenario
-from minifi_test_framework.core.hooks import common_after_scenario
+from minifi_behave.core.hooks import common_before_scenario
+from minifi_behave.core.hooks import common_after_scenario
 
 # These hooks are executed by behave before and after each scenario
 # The common_before_scenario and common_after_scenario must be called for proper setup and tear down
diff --git a/extensions/standard-processors/tests/features/steps/minifi_c2_server_container.py b/extensions/standard-processors/tests/features/steps/minifi_c2_server_container.py
index ff1d5d352..a645499f6 100644
--- a/extensions/standard-processors/tests/features/steps/minifi_c2_server_container.py
+++ b/extensions/standard-processors/tests/features/steps/minifi_c2_server_container.py
@@ -23,13 +23,13 @@ from cryptography import x509
 from pathlib import Path
 
 from cryptography.hazmat.primitives.serialization import load_pem_private_key, pkcs12, BestAvailableEncryption
-from minifi_test_framework.containers.container_linux import LinuxContainer
-from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.core.ssl_utils import make_server_cert
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.containers.host_file import HostFile
-from minifi_test_framework.core.ssl_utils import dump_key, dump_cert
+from minifi_behave.containers.container_linux import LinuxContainer
+from minifi_behave.core.helpers import wait_for_condition
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.ssl_utils import make_server_cert
+from minifi_behave.containers.file import File
+from minifi_behave.containers.host_file import HostFile
+from minifi_behave.core.ssl_utils import dump_key, dump_cert
 
 
 class MinifiC2Server(LinuxContainer):
diff --git a/extensions/standard-processors/tests/features/steps/minifi_controller_steps.py b/extensions/standard-processors/tests/features/steps/minifi_controller_steps.py
index 02aea2c22..f4dca56f8 100644
--- a/extensions/standard-processors/tests/features/steps/minifi_controller_steps.py
+++ b/extensions/standard-processors/tests/features/steps/minifi_controller_steps.py
@@ -16,8 +16,8 @@
 #
 
 from behave import given, when, then
-from minifi_test_framework.core.helpers import retry_check
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.helpers import retry_check
+from minifi_behave.core.minifi_test_context import MinifiTestContext
 
 
 @given('controller socket properties are set up')
diff --git a/extensions/standard-processors/tests/features/steps/steps.py b/extensions/standard-processors/tests/features/steps/steps.py
index 79167ed56..689a6aef9 100644
--- a/extensions/standard-processors/tests/features/steps/steps.py
+++ b/extensions/standard-processors/tests/features/steps/steps.py
@@ -18,17 +18,17 @@
 import humanfriendly
 from behave import step, then, given
 
-from minifi_test_framework.steps import checking_steps        # noqa: F401
-from minifi_test_framework.steps import configuration_steps   # noqa: F401
-from minifi_test_framework.steps import core_steps            # noqa: F401
-from minifi_test_framework.steps import flow_building_steps   # noqa: F401
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.core.helpers import wait_for_condition
+from minifi_behave.steps import checking_steps        # noqa: F401
+from minifi_behave.steps import configuration_steps   # noqa: F401
+from minifi_behave.steps import core_steps            # noqa: F401
+from minifi_behave.steps import flow_building_steps   # noqa: F401
+from minifi_behave.core.minifi_test_context import MinifiTestContext
+from minifi_behave.core.helpers import wait_for_condition
 from containers.syslog_container import SyslogContainer
 from containers.diag_slave_container import DiagSlave
 from containers.tcp_client_container import TcpClientContainer
 
-from minifi_test_framework.containers.minifi_protocol import set_up_ssl_properties
+from minifi_behave.containers.minifi_protocol import set_up_ssl_properties
 from minifi_c2_server_container import MinifiC2Server
 
 


(nifi-minifi-cpp) 02/03: MINIFICPP-2718 Windows based docker tests #2133

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

martinzink pushed a commit to branch apache-rusty
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git

commit 05d243fd9f549fa65db14a7b2760357ef4b1f917
Author: Martin Zink <ma...@apache.org>
AuthorDate: Tue Mar 31 08:14:23 2026 +0200

    MINIFICPP-2718 Windows based docker tests #2133
---
 behave_framework/pyproject.toml                    |   5 +-
 .../{container.py => container_linux.py}           |  39 +-
 .../containers/container_protocol.py               |  62 ++++
 .../containers/container_windows.py                | 392 +++++++++++++++++++++
 .../containers/http_proxy_container.py             |   4 +-
 .../containers/minifi_container.py                 | 231 ------------
 .../containers/minifi_controller.py                |  95 +++++
 .../containers/minifi_linux_container.py           | 146 ++++++++
 .../containers/minifi_protocol.py                  |  58 +++
 .../containers/minifi_win_container.py             |  75 ++++
 .../containers/nifi_container.py                   |  13 +-
 .../src/minifi_test_framework/core/hooks.py        |  12 +-
 .../core/minifi_test_context.py                    |  34 +-
 .../src/minifi_test_framework/core/ssl_utils.py    | 166 +++------
 .../steps/configuration_steps.py                   |  12 +-
 docker/RunBehaveTests.sh                           |  21 +-
 docker/installed/win.Dockerfile                    |  42 +++
 .../containers/kinesis_server_container.py         |   4 +-
 .../features/containers/s3_server_container.py     |   4 +-
 .../features/containers/azure_server_container.py  |   4 +-
 .../containers/couchbase_server_container.py       |  17 +-
 .../features/containers/elastic_base_container.py  |   4 +-
 .../features/containers/elasticsearch_container.py |  13 +-
 .../features/containers/opensearch_container.py    |   9 +-
 .../containers/fake_gcs_server_container.py        |   4 +-
 .../features/containers/grafana_loki_container.py  |  13 +-
 .../features/containers/reverse_proxy_container.py |   4 +-
 .../features/containers/kafka_server_container.py  |  13 +-
 .../features/containers/mqtt_broker_container.py   |   4 +-
 .../features/containers/opc_ua_server_container.py |   4 +-
 .../features/containers/prometheus_container.py    |  12 +-
 .../tests/features/containers/splunk_container.py  |  13 +-
 .../containers/postgress_server_container.py       |   4 +-
 .../features/containers/diag_slave_container.py    |   4 +-
 .../tests/features/containers/syslog_container.py  |   4 +-
 .../features/containers/tcp_client_container.py    |   4 +-
 .../tests/features/core_functionality.feature      |   6 +-
 .../tests/features/environment.py                  |   5 +-
 .../tests/features/replace_text.feature            |   2 +-
 .../features/steps/minifi_c2_server_container.py   |  16 +-
 .../features/steps/minifi_controller_steps.py      |  28 +-
 .../tests/features/steps/steps.py                  |   4 +-
 42 files changed, 1101 insertions(+), 505 deletions(-)

diff --git a/behave_framework/pyproject.toml b/behave_framework/pyproject.toml
index e9484c285..8bb2a83e1 100644
--- a/behave_framework/pyproject.toml
+++ b/behave_framework/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "minifi-test-framework"
-version = "0.1.0"
+version = "0.2.0"
 requires-python = ">= 3.10"
 description = "A testing framework for MiNiFi extensions."
 dependencies = [
@@ -8,8 +8,7 @@ dependencies = [
     "docker==7.1.0",
     "PyYAML==6.0.3",
     "humanfriendly==10.0",
-    "m2crypto==0.46.2",
-    "pyopenssl==26.0.0",
+    "cryptography==46.0.5",
     "pyjks==20.0.0"
 ]
 
diff --git a/behave_framework/src/minifi_test_framework/containers/container.py b/behave_framework/src/minifi_test_framework/containers/container_linux.py
similarity index 93%
rename from behave_framework/src/minifi_test_framework/containers/container.py
rename to behave_framework/src/minifi_test_framework/containers/container_linux.py
index 6beababde..6d49a104d 100644
--- a/behave_framework/src/minifi_test_framework/containers/container.py
+++ b/behave_framework/src/minifi_test_framework/containers/container_linux.py
@@ -15,29 +15,31 @@
 #  limitations under the License.
 #
 from __future__ import annotations
-from typing import TYPE_CHECKING
 
 import json
 import logging
 import os
 import shlex
-import tempfile
 import tarfile
+import tempfile
 import uuid
-
-import docker
 from docker.models.networks import Network
-
+from minifi_test_framework.containers.container_protocol import ContainerProtocol
 from minifi_test_framework.containers.directory import Directory
 from minifi_test_framework.containers.file import File
 from minifi_test_framework.containers.host_file import HostFile
+from typing import TYPE_CHECKING
+
+import docker
 
 if TYPE_CHECKING:
     from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 
 
-class Container:
-    def __init__(self, image_name: str, container_name: str, network: Network, command: str | None = None, entrypoint: str | None = None):
+class LinuxContainer(ContainerProtocol):
+    def __init__(self, image_name: str, container_name: str, network: Network, command: str | None = None,
+                 entrypoint: str | None = None):
+        super().__init__()
         self.image_name: str = image_name
         self.container_name: str = container_name
         self.network: Network = network
@@ -121,10 +123,11 @@ class Container:
             pass
         try:
             logging.info(f"Creating and starting container '{self.container_name}'...")
-            self.container = self.client.containers.run(
-                image=self.image_name, name=self.container_name, ports=self.ports,
-                environment=self.environment, volumes=self.volumes, network=self.network.name,
-                command=self.command, entrypoint=self.entrypoint, user=self.user, detach=True)
+            self.container = self.client.containers.run(image=self.image_name, name=self.container_name,
+                                                        ports=self.ports, environment=self.environment,
+                                                        volumes=self.volumes, network=self.network.name,
+                                                        command=self.command, entrypoint=self.entrypoint,
+                                                        user=self.user, detach=True)
         except Exception as e:
             logging.error(f"Error starting container: {e}")
             raise
@@ -212,7 +215,8 @@ class Container:
         exit_code, output = self.exec_run("sh -c {}".format(shlex.quote(command)))
 
         if exit_code != 0:
-            logging.debug("While looking for regex %s in directory %s, grep returned exit code %d, output: %s", regex_str, directory_path, exit_code, output)
+            logging.debug("While looking for regex %s in directory %s, grep returned exit code %d, output: %s",
+                          regex_str, directory_path, exit_code, output)
         return exit_code == 0
 
     def path_with_content_exists(self, path: str, content: str) -> bool:
@@ -376,7 +380,8 @@ class Container:
 
             return os.path.join(temp_dir, os.path.basename(directory_path.strip('/')))
         except Exception as e:
-            logging.error(f"Error extracting files from directory path '{directory_path}' from container '{self.container_name}': {e}")
+            logging.error(
+                f"Error extracting files from directory path '{directory_path}' from container '{self.container_name}': {e}")
             return None
 
     def _read_files_from_directory(self, directory_path: str) -> list[str] | None:
@@ -479,3 +484,11 @@ class Container:
             return True
 
         return False
+
+    def get_memory_usage(self) -> int | None:
+        exit_code, output = self.exec_run(["awk", "/VmRSS/ { printf \"%d\\n\", $2 }", "/proc/1/status"])
+        if exit_code != 0:
+            return None
+        memory_usage_in_bytes = int(output.strip()) * 1024
+        logging.info(f"{self.container_name} memory usage: {memory_usage_in_bytes} bytes")
+        return memory_usage_in_bytes
diff --git a/behave_framework/src/minifi_test_framework/containers/container_protocol.py b/behave_framework/src/minifi_test_framework/containers/container_protocol.py
new file mode 100644
index 000000000..fdbdf2861
--- /dev/null
+++ b/behave_framework/src/minifi_test_framework/containers/container_protocol.py
@@ -0,0 +1,62 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import Protocol, List
+
+from minifi_test_framework.containers.directory import Directory
+from minifi_test_framework.containers.file import File
+from minifi_test_framework.containers.host_file import HostFile
+
+
+class ContainerProtocol(Protocol):
+    image_name: str
+    container_name: str
+    dirs: List[Directory]
+    files: List[File]
+    host_files: List[HostFile]
+
+    def deploy(self, context) -> bool:
+        ...
+
+    def clean_up(self):
+        ...
+
+    def exec_run(self, command):
+        ...
+
+    def directory_contains_file_with_content(self, directory_path: str, expected_content: str) -> bool:
+        ...
+
+    def directory_contains_file_with_regex(self, directory_path: str, regex_str: str) -> bool:
+        ...
+
+    def path_with_content_exists(self, path: str, content: str) -> bool:
+        ...
+
+    def get_logs(self) -> str:
+        ...
+
+    @property
+    def exited(self) -> bool:
+        ...
+
+    def get_number_of_files(self, directory_path: str) -> int:
+        ...
+
+    def verify_file_contents(self, directory_path: str, expected_contents: list[str]) -> bool:
+        ...
+
+    def log_app_output(self) -> bool:
+        ...
diff --git a/behave_framework/src/minifi_test_framework/containers/container_windows.py b/behave_framework/src/minifi_test_framework/containers/container_windows.py
new file mode 100644
index 000000000..c1a9c755a
--- /dev/null
+++ b/behave_framework/src/minifi_test_framework/containers/container_windows.py
@@ -0,0 +1,392 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+from __future__ import annotations
+import logging
+import os
+import tempfile
+import base64
+import tarfile
+import io
+from typing import Union, Optional, Tuple, List, Dict, TYPE_CHECKING
+
+import docker
+from docker.models.networks import Network
+from docker.models.containers import Container
+
+from minifi_test_framework.containers.container_protocol import ContainerProtocol
+from minifi_test_framework.containers.directory import Directory
+from minifi_test_framework.containers.file import File
+from minifi_test_framework.containers.host_file import HostFile
+
+if TYPE_CHECKING:
+    from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+
+
+class WindowsContainer(ContainerProtocol):
+    def __init__(self, image_name: str, container_name: str, network: Network, command: str | None = None, entrypoint: str | None = None):
+        super().__init__()
+        self.image_name: str = image_name
+        self.container_name: str = container_name
+        self.network: Network = network
+
+        self.client: docker.DockerClient = docker.from_env()
+        self.container: Optional[Container] = None
+        self.files: List[File] = []
+        self.dirs: List[Directory] = []
+        self.host_files: List[HostFile] = []
+        self.volumes: Dict = {}
+        self.command: str | None = command
+        self.entrypoint: str | None = entrypoint
+        self._temp_dir: Optional[tempfile.TemporaryDirectory] = None
+        self.ports: Optional[Dict[str, int]] = None
+        self.environment: List[str] = []
+
+    def _normalize_path(self, path: str) -> str:
+        clean_path = path.strip().replace("/", "\\")
+        if clean_path.startswith("\\"):
+            clean_path = clean_path[1:]
+
+        # If it doesn't already have a drive letter, assume C:
+        if ":" not in clean_path:
+            return f"C:\\{clean_path}"
+        return clean_path
+
+    def deploy(self, context: MinifiTestContext | None) -> bool:
+        if self._temp_dir:
+            self._temp_dir.cleanup()
+        self._temp_dir = tempfile.TemporaryDirectory()
+
+        for directory in self.dirs:
+            rel_path = directory.path.strip("/\\")
+            temp_subdir = os.path.join(self._temp_dir.name, rel_path)
+            os.makedirs(temp_subdir, exist_ok=True)
+
+            for file_name, content in directory.files.items():
+                file_path = os.path.join(temp_subdir, file_name)
+                with open(file_path, "w", encoding="utf-8") as temp_file:
+                    logging.info(f"writing content into {temp_file.name}")
+                    temp_file.write(content)
+
+            container_bind_path = self._normalize_path(directory.path)
+            self.volumes[temp_subdir] = {
+                "bind": container_bind_path,
+                "mode": directory.mode
+            }
+
+        for host_file in self.host_files:
+            container_bind_path = self._normalize_path(host_file.container_path)
+            self.volumes[host_file.host_path] = {"bind": container_bind_path, "mode": host_file.mode}
+
+        try:
+            existing_container = self.client.containers.get(self.container_name)
+            logging.warning(f"Found existing container '{self.container_name}'. Removing it first.")
+            existing_container.remove(force=True)
+        except docker.errors.NotFound:
+            pass
+
+        try:
+            logging.info(f"Creating and starting container '{self.container_name}'...")
+            self.container = self.client.containers.create(
+                image=self.image_name,
+                name=self.container_name,
+                ports=self.ports,
+                environment=self.environment,
+                volumes=self.volumes,
+                network=self.network.name,
+                command=self.command,
+                entrypoint=self.entrypoint,
+                detach=True,
+                tty=False
+            )
+
+            self.container.start()
+
+            for file in self.files:
+                self._copy_content_to_container(file.content, file.path)
+
+        except Exception as e:
+            logging.error(f"Error starting container: {e}")
+            self.clean_up()
+            raise
+        return True
+
+    def _copy_content_to_container(self, content: str | bytes, target_path: str):
+        if not self.container:
+            return
+
+        win_path = self._normalize_path(target_path)
+        dir_name = os.path.dirname(win_path)
+        file_name = os.path.basename(win_path)
+
+        self._run_powershell(f"New-Item -ItemType Directory -Force -Path '{dir_name}'")
+
+        tar_stream = io.BytesIO()
+        with tarfile.open(fileobj=tar_stream, mode='w') as tar:
+            if isinstance(content, str):
+                encoded_data = content.encode('utf-8')
+            else:
+                encoded_data = content
+            tarinfo = tarfile.TarInfo(name=file_name)
+            tarinfo.size = len(encoded_data)
+            tar.addfile(tarinfo, io.BytesIO(encoded_data))
+
+        tar_stream.seek(0)
+
+        self.container.put_archive(path=dir_name, data=tar_stream)
+
+    def clean_up(self):
+        if self._temp_dir:
+            try:
+                self._temp_dir.cleanup()
+                self._temp_dir = None
+            except Exception as e:
+                logging.warning(f"Failed to cleanup temp dir: {e}")
+
+        if self.container:
+            try:
+                self.container.remove(force=True)
+            except docker.errors.NotFound:
+                pass
+            except Exception as e:
+                logging.warning(f"Failed to remove container: {e}")
+            finally:
+                self.container = None
+
+    def exec_run(self, command: Union[str, list]) -> Tuple[int | None, str]:
+        logging.debug(f"Running command: {command}")
+        if self.container:
+            (code, output) = self.container.exec_run(command, detach=False)
+            decoded_output = output.decode("utf-8", errors='replace')
+            logging.debug(f"Result {code}, output: {decoded_output}")
+            return code, decoded_output
+        return None, "Container not running."
+
+    def _run_powershell(self, ps_script: str) -> Tuple[int | None, str]:
+        if not self.container:
+            return None, "Container not running"
+
+        encoded_command = base64.b64encode(ps_script.encode('utf_16_le')).decode('utf-8')
+
+        cmd_parts = ["powershell", "-NonInteractive", "-NoProfile", "-EncodedCommand", encoded_command]
+
+        return self.exec_run(cmd_parts)
+
+    def not_empty_dir_exists(self, directory_path: str) -> bool:
+        if not self.container:
+            return False
+
+        win_path = self._normalize_path(directory_path)
+        ps_script = (
+            f"if (Test-Path -Path '{win_path}' -PathType Container) {{ "
+            f"  if (Get-ChildItem -Path '{win_path}') {{ exit 0 }} else {{ exit 1 }} "
+            f"}} else {{ exit 2 }}"
+        )
+
+        exit_code, _ = self._run_powershell(ps_script)
+        return exit_code == 0
+
+    def directory_contains_file_with_content(self, directory_path: str, expected_content: str) -> bool:
+        if not self.container:
+            return False
+
+        win_path = self._normalize_path(directory_path)
+        escaped_content = expected_content.replace("'", "''")
+
+        ps_script = (
+            f"if (-not (Test-Path '{win_path}')) {{ exit 2 }}; "
+            f"$matches = Get-ChildItem -Path '{win_path}' -File -Depth 0 | "
+            f"Select-String -Pattern '{escaped_content}' -SimpleMatch -List; "
+            f"if ($matches) {{ exit 0 }} else {{ exit 1 }}"
+        )
+
+        exit_code, _ = self._run_powershell(ps_script)
+        return exit_code == 0
+
+    def directory_contains_file_with_regex(self, directory_path: str, regex_str: str) -> bool:
+        if not self.container:
+            return False
+
+        win_path = self._normalize_path(directory_path)
+        escaped_regex = regex_str.replace("'", "''")
+
+        ps_script = (
+            f"if (-not (Test-Path '{win_path}')) {{ exit 2 }}; "
+            f"$matches = Get-ChildItem -Path '{win_path}' -File -Depth 0 | "
+            f"Select-String -Pattern '{escaped_regex}' -List; "
+            f"if ($matches) {{ exit 0 }} else {{ exit 1 }}"
+        )
+
+        exit_code, _ = self._run_powershell(ps_script)
+        return exit_code == 0
+
+    def path_with_content_exists(self, path: str, content: str) -> bool:
+        if not self.container:
+            return False
+
+        win_path = self._normalize_path(path)
+
+        escaped_content = content.replace("'", "''").replace("\n", "\r\n")
+
+        ps_script = (
+            f"try {{ "
+            f"  $found = Select-String -Path '{win_path}' -Pattern '^{escaped_content}$'; "
+            f"  if ($found.Count -eq 1) {{ exit 0 }} else {{ exit 1 }} "
+            f"}} catch {{ exit 2 }}"
+        )
+
+        exit_code, output = self._run_powershell(ps_script)
+        if exit_code != 0:
+            logging.debug(f"path_with_content_exists failed for {win_path}. Output: {output}")
+
+        return exit_code == 0
+
+    def directory_has_single_file_with_content(self, directory_path: str, expected_content: str) -> bool:
+        if not self.container:
+            return False
+
+        win_path = self._normalize_path(directory_path)
+        escaped_content = expected_content.strip().replace("'", "''").replace("\n", "\r\n")
+
+        ps_script = (
+            f"$files = Get-ChildItem -Path '{win_path}' -File -Depth 0; "
+            f"if ($files.Count -ne 1) {{ exit 1 }}; "
+            f"$actual_content = Get-Content -Path $files[0].FullName -Raw; "
+            f"if ($actual_content.Trim() -eq '{escaped_content}') {{ exit 0 }} else {{ exit 2 }};"
+        )
+
+        exit_code, output = self._run_powershell(ps_script)
+        if exit_code != 0:
+            logging.debug(f"Check for single file failed in {win_path}. Output: {output}")
+
+        return exit_code == 0
+
+    def get_logs(self) -> str:
+        logging.debug("Getting logs from container '%s'", self.container_name)
+        if not self.container:
+            return ""
+        logs_as_bytes = self.container.logs()
+        return logs_as_bytes.decode('utf-8', errors='replace')
+
+    def log_app_output(self) -> bool:
+        logs = self.get_logs()
+        logging.info("Logs of container '%s':", self.container_name)
+        for line in logs.splitlines():
+            logging.info(line)
+        return False
+
+    @property
+    def exited(self) -> bool:
+        if not self.container:
+            return False
+        try:
+            self.container.reload()
+            return self.container.status == 'exited'
+        except docker.errors.NotFound:
+            self.container = None
+            return False
+        except Exception:
+            return False
+
+    def get_number_of_files(self, directory_path: str) -> int:
+        if not self.container:
+            return -1
+
+        win_path = self._normalize_path(directory_path)
+        ps_script = f"(Get-ChildItem -Path '{win_path}' -File -Depth 0).Count"
+
+        exit_code, output = self._run_powershell(ps_script)
+
+        if exit_code != 0:
+            logging.error(f"Error counting files in '{win_path}': {output}")
+            return -1
+
+        try:
+            return int(output.strip())
+        except (ValueError, IndexError):
+            return -1
+
+    def verify_file_contents(self, directory_path: str, expected_contents: List[str]) -> bool:
+        if not self.container:
+            return False
+
+        win_path = self._normalize_path(directory_path)
+
+        ps_list = f"Get-ChildItem -Path '{win_path}' -File -Depth 0 | Select-Object -ExpandProperty FullName"
+        exit_code, output = self._run_powershell(ps_list)
+
+        if exit_code != 0:
+            logging.error(f"Error listing files in '{win_path}': {output}")
+            return False
+
+        actual_filepaths = [path.strip() for path in output.splitlines() if path.strip()]
+
+        if len(actual_filepaths) != len(expected_contents):
+            return False
+
+        actual_file_contents = []
+        for path in actual_filepaths:
+            ps_read = (
+                f"$c = Get-Content -Path '{path}' -Raw; "
+                f"if ($c) {{ ($c -replace '\\r\\n', '\\n' -replace '\\r', '\\n').Trim() }}"
+            )
+
+            exit_code, content = self._run_powershell(ps_read)
+            if exit_code != 0:
+                return False
+            actual_file_contents.append(content)
+
+        normalized_expected = [
+            s.strip().replace("\r\n", "\n").replace("\r", "\n") for s in expected_contents
+        ]
+
+        return sorted(actual_file_contents) == sorted(normalized_expected)
+
+    def nonempty_dir_exists(self, directory_path: str) -> bool:
+        if not self.container:
+            return False
+
+        win_path = self._normalize_path(directory_path)
+        ps_script = (
+            f"if ((Test-Path -LiteralPath '{win_path}' -PathType Container) "
+            f"-and (Get-ChildItem -LiteralPath '{win_path}' -Force | Select-Object -First 1)) "
+            f"{{ exit 0 }} else {{ exit 1 }}"
+        )
+
+        exit_code, _ = self._run_powershell(ps_script)
+
+        return exit_code == 0
+
+    def directory_contains_file_with_minimum_size(self, directory_path: str, expected_size: int) -> bool:
+        if not self.container or not self.nonempty_dir_exists(directory_path):
+            return False
+
+        win_path = self._normalize_path(directory_path)
+        ps_script = (
+            f"Get-ChildItem -LiteralPath '{win_path}' -File "
+            f"| Where-Object {{ $_.Length -gt {expected_size} }}"
+        )
+
+        exit_code, output = self._run_powershell(ps_script)
+
+        if exit_code != 0:
+            logging.error(f"Error running command to get file sizes: {output}")
+            return False
+
+        if output and len(output.strip()) > 0:
+            return True
+
+        return False
diff --git a/behave_framework/src/minifi_test_framework/containers/http_proxy_container.py b/behave_framework/src/minifi_test_framework/containers/http_proxy_container.py
index da0cd4c2b..430e2abff 100644
--- a/behave_framework/src/minifi_test_framework/containers/http_proxy_container.py
+++ b/behave_framework/src/minifi_test_framework/containers/http_proxy_container.py
@@ -17,13 +17,13 @@
 
 from textwrap import dedent
 
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
 from minifi_test_framework.core.helpers import wait_for_condition
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 
 
-class HttpProxy(Container):
+class HttpProxy(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext):
         dockerfile = dedent("""\
                 FROM {base_image}
diff --git a/behave_framework/src/minifi_test_framework/containers/minifi_container.py b/behave_framework/src/minifi_test_framework/containers/minifi_container.py
deleted file mode 100644
index 8361d927c..000000000
--- a/behave_framework/src/minifi_test_framework/containers/minifi_container.py
+++ /dev/null
@@ -1,231 +0,0 @@
-#
-#  Licensed to the Apache Software Foundation (ASF) under one or more
-#  contributor license agreements.  See the NOTICE file distributed with
-#  this work for additional information regarding copyright ownership.
-#  The ASF licenses this file to You under the Apache License, Version 2.0
-#  (the "License"); you may not use this file except in compliance with
-#  the License.  You may obtain a copy of the License at
-#
-#      http://www.apache.org/licenses/LICENSE-2.0
-#
-#  Unless required by applicable law or agreed to in writing, software
-#  distributed under the License is distributed on an "AS IS" BASIS,
-#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-#  See the License for the specific language governing permissions and
-#  limitations under the License.
-#
-import logging
-import os
-from pathlib import Path
-from OpenSSL import crypto
-
-from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.containers.file import File
-from minifi_test_framework.containers.host_file import HostFile
-from minifi_test_framework.minifi.minifi_flow_definition import MinifiFlowDefinition
-from minifi_test_framework.core.ssl_utils import make_cert_without_extended_usage, make_client_cert, make_server_cert
-from minifi_test_framework.core.helpers import wait_for_condition, retry_check
-from .container import Container
-
-
-class MinifiContainer(Container):
-    def __init__(self, container_name: str, test_context: MinifiTestContext):
-        super().__init__(test_context.minifi_container_image, f"{container_name}-{test_context.scenario_id}", test_context.network)
-        self.flow_definition = MinifiFlowDefinition()
-        self.properties: dict[str, str] = {}
-        self.log_properties: dict[str, str] = {}
-        self.scenario_id = test_context.scenario_id
-        self.deploy_timeout_seconds = 20
-
-        minifi_client_cert, minifi_client_key = make_cert_without_extended_usage(common_name=self.container_name, ca_cert=test_context.root_ca_cert, ca_key=test_context.root_ca_key)
-        self.files.append(File("/usr/local/share/certs/ca-root-nss.crt", crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=test_context.root_ca_cert)))
-        self.files.append(File("/tmp/resources/root_ca.crt", crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=test_context.root_ca_cert)))
-        self.files.append(File("/tmp/resources/minifi_client.crt", crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=minifi_client_cert)))
-        self.files.append(File("/tmp/resources/minifi_client.key", crypto.dump_privatekey(type=crypto.FILETYPE_PEM, pkey=minifi_client_key)))
-        self.files.append(File("/tmp/resources/minifi_merged_cert.crt",
-                               crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=minifi_client_cert) + crypto.dump_privatekey(type=crypto.FILETYPE_PEM, pkey=minifi_client_key)))
-
-        clientuser_cert, clientuser_key = make_client_cert("clientuser", ca_cert=test_context.root_ca_cert, ca_key=test_context.root_ca_key)
-        self.files.append(File("/tmp/resources/clientuser.crt", crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=clientuser_cert)))
-        self.files.append(File("/tmp/resources/clientuser.key", crypto.dump_privatekey(type=crypto.FILETYPE_PEM, pkey=clientuser_key)))
-
-        minifi_server_cert, minifi_server_key = make_server_cert(common_name=f"server-{test_context.scenario_id}", ca_cert=test_context.root_ca_cert, ca_key=test_context.root_ca_key)
-        self.files.append(File("/tmp/resources/minifi_server.crt",
-                               crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=minifi_server_cert) + crypto.dump_privatekey(type=crypto.FILETYPE_PEM, pkey=minifi_server_key)))
-
-        self.is_fhs = 'MINIFI_INSTALLATION_TYPE=FHS' in str(self.client.images.get(test_context.minifi_container_image).history())
-        if self.is_fhs:
-            self.minifi_controller_path = '/usr/bin/minifi-controller'
-        else:
-            self.minifi_controller_path = '/opt/minifi/minifi-current/bin/minifi-controller'
-
-        self._fill_default_properties()
-        self._fill_default_log_properties()
-
-    def deploy(self, context: MinifiTestContext | None) -> bool:
-        flow_config = self.flow_definition.to_yaml()
-        logging.info(f"Deploying MiNiFi container '{self.container_name}' with flow configuration:\n{flow_config}")
-        if self.is_fhs:
-            self.files.append(File("/etc/nifi-minifi-cpp/config.yml", flow_config))
-            self.files.append(File("/etc/nifi-minifi-cpp/minifi.properties", self._get_properties_file_content()))
-            self.files.append(
-                File("/etc/nifi-minifi-cpp/minifi-log.properties", self._get_log_properties_file_content()))
-        else:
-            self.files.append(File("/opt/minifi/minifi-current/conf/config.yml", flow_config))
-            self.files.append(
-                File("/opt/minifi/minifi-current/conf/minifi.properties", self._get_properties_file_content()))
-            self.files.append(File("/opt/minifi/minifi-current/conf/minifi-log.properties",
-                                   self._get_log_properties_file_content()))
-
-        resource_dir = Path(__file__).resolve().parent / "resources" / "minifi-controller"
-        self.host_files.append(HostFile("/tmp/resources/minifi-controller/config.yml", os.path.join(resource_dir, "config.yml")))
-
-        if not super().deploy(context):
-            return False
-
-        finished_str = "MiNiFi started"
-        return wait_for_condition(
-            condition=lambda: finished_str in self.get_logs(),
-            timeout_seconds=self.deploy_timeout_seconds,
-            bail_condition=lambda: self.exited,
-            context=context)
-
-    def set_deploy_timeout_seconds(self, timeout_seconds: int):
-        self.deploy_timeout_seconds = timeout_seconds
-
-    def set_property(self, key: str, value: str):
-        self.properties[key] = value
-
-    def set_log_property(self, key: str, value: str):
-        self.log_properties[key] = value
-
-    def enable_openssl_fips_mode(self):
-        self.properties["nifi.openssl.fips.support.enable"] = "true"
-
-    def enable_log_metrics_publisher(self):
-        self.properties["nifi.metrics.publisher.LogMetricsPublisher.metrics"] = "RepositoryMetrics"
-        self.properties["nifi.metrics.publisher.LogMetricsPublisher.logging.interval"] = "1s"
-        self.properties["nifi.metrics.publisher.class"] = "LogMetricsPublisher"
-
-    def configure_c2_flow_url(self):
-        self.properties["nifi.c2.flow.url"] = f"http://minifi-c2-server-{self.scenario_id}:10090/c2/config?class=minifi-test-class"
-
-    def set_up_ssl_properties(self):
-        self.properties["nifi.remote.input.secure"] = "true"
-        self.properties["nifi.security.client.certificate"] = "/tmp/resources/minifi_client.crt"
-        self.properties["nifi.security.client.private.key"] = "/tmp/resources/minifi_client.key"
-        self.properties["nifi.security.client.ca.certificate"] = "/tmp/resources/root_ca.crt"
-
-    def _fill_default_properties(self):
-        if self.is_fhs:
-            self.properties["nifi.flow.configuration.file"] = "/etc/nifi-minifi-cpp/config.yml"
-            self.properties["nifi.extension.path"] = "/usr/lib64/nifi-minifi-cpp/extensions/*"
-            self.properties["nifi.python.processor.dir"] = '/var/lib/nifi-minifi-cpp/minifi-python'
-        else:
-            self.properties["nifi.flow.configuration.file"] = "./conf/config.yml"
-            self.properties["nifi.extension.path"] = "../extensions/*"
-            self.properties["nifi.python.processor.dir"] = '/opt/minifi/minifi-current/minifi-python'
-        self.properties["nifi.administrative.yield.duration"] = "1 sec"
-        self.properties["nifi.bored.yield.duration"] = "100 millis"
-        self.properties["nifi.openssl.fips.support.enable"] = "false"
-        self.properties["nifi.provenance.repository.class.name"] = "NoOpRepository"
-
-    def _fill_default_log_properties(self):
-        self.log_properties["spdlog.pattern"] = "[%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v"
-
-        self.log_properties["appender.stderr"] = "stderr"
-        self.log_properties["logger.root"] = "DEBUG, stderr"
-        self.log_properties["logger.org::apache::nifi::minifi"] = "DEBUG, stderr"
-
-    def _get_properties_file_content(self):
-        lines = (f"{key}={value}" for key, value in self.properties.items())
-        return "\n".join(lines)
-
-    def _get_log_properties_file_content(self):
-        lines = (f"{key}={value}" for key, value in self.log_properties.items())
-        return "\n".join(lines)
-
-    def get_memory_usage(self) -> int | None:
-        exit_code, output = self.exec_run(["awk", "/VmRSS/ { printf \"%d\\n\", $2 }", "/proc/1/status"])
-        if exit_code != 0:
-            return None
-        memory_usage_in_bytes = int(output.strip()) * 1024
-        logging.info(f"MiNiFi memory usage: {memory_usage_in_bytes} bytes")
-        return memory_usage_in_bytes
-
-    def set_controller_socket_properties(self):
-        self.properties["controller.socket.enable"] = "true"
-        self.properties["controller.socket.host"] = "localhost"
-        self.properties["controller.socket.port"] = "9998"
-        self.properties["controller.socket.local.any.interface"] = "false"
-
-    def update_flow_config_through_controller(self):
-        self.exec_run([self.minifi_controller_path, "--updateflow", "/tmp/resources/minifi-controller/config.yml"])
-
-    def updated_config_is_persisted(self) -> bool:
-        exit_code, output = self.exec_run(["cat", "/opt/minifi/minifi-current/conf/config.yml" if not self.is_fhs else "/etc/nifi-minifi-cpp/config.yml"])
-        if exit_code != 0:
-            logging.error("Failed to read MiNiFi config file to check if updated config is persisted")
-            return False
-        return "2f2a3b47-f5ba-49f6-82b5-bc1c86b96f38" in output
-
-    def stop_component_through_controller(self, component: str):
-        self.exec_run([self.minifi_controller_path, "--stop", component])
-
-    def start_component_through_controller(self, component: str):
-        self.exec_run([self.minifi_controller_path, "--start", component])
-
-    @retry_check(10, 1)
-    def is_component_running(self, component: str) -> bool:
-        (code, output) = self.exec_run([self.minifi_controller_path, "--list", "components"])
-        return code == 0 and component + ", running: true" in output
-
-    def get_connections(self):
-        (_, output) = self.exec_run([self.minifi_controller_path, "--list", "connections"])
-        connections = []
-        for line in output.split('\n'):
-            if not line.startswith('[') and not line.startswith('Connection Names'):
-                connections.append(line)
-        return connections
-
-    @retry_check(10, 1)
-    def connection_found_through_controller(self, connection: str) -> bool:
-        return connection in self.get_connections()
-
-    def get_full_connection_count(self) -> int:
-        (_, output) = self.exec_run([self.minifi_controller_path, "--getfull"])
-        for line in output.split('\n'):
-            if "are full" in line:
-                return int(line.split(' ')[0])
-        return -1
-
-    def get_connection_size(self, connection: str):
-        (_, output) = self.exec_run([self.minifi_controller_path, "--getsize", connection])
-        for line in output.split('\n'):
-            if "Size/Max of " + connection in line:
-                size_and_max = line.split(connection)[1].split('/')
-                return (int(size_and_max[0].strip()), int(size_and_max[1].strip()))
-        return (-1, -1)
-
-    def get_manifest(self) -> str:
-        (_, output) = self.exec_run([self.minifi_controller_path, "--manifest"])
-        manifest = ""
-        for line in output.split('\n'):
-            if not line.startswith('['):
-                manifest += line
-        return manifest
-
-    def create_debug_bundle(self) -> bool:
-        (code, _) = self.exec_run([self.minifi_controller_path, "--debug", "/tmp"])
-        if code != 0:
-            logging.error("Minifi controller debug command failed with code: %d", code)
-            return False
-
-        (code, _) = self.exec_run(["test", "-f", "/tmp/debug.tar.gz"])
-        return code == 0
-
-    def add_example_python_processors(self):
-        run_minifi_cmd = '/opt/minifi/minifi-current/bin/minifi.sh run' if not self.is_fhs else '/usr/bin/minifi'
-        minifi_python_dir_path = '/opt/minifi/minifi-current/minifi-python' if not self.is_fhs else '/var/lib/nifi-minifi-cpp/minifi-python'
-        minifi_python_examples = '/opt/minifi/minifi-current/minifi-python-examples' if not self.is_fhs else '/usr/share/doc/nifi-minifi-cpp/pythonprocessor-examples'
-        self.command = f'sh -c "cp -r {minifi_python_examples} {minifi_python_dir_path}/examples && {run_minifi_cmd}"'
diff --git a/behave_framework/src/minifi_test_framework/containers/minifi_controller.py b/behave_framework/src/minifi_test_framework/containers/minifi_controller.py
new file mode 100644
index 000000000..59f6e4028
--- /dev/null
+++ b/behave_framework/src/minifi_test_framework/containers/minifi_controller.py
@@ -0,0 +1,95 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import logging
+from minifi_test_framework.core.helpers import retry_check
+
+
+class MinifiController:
+    def __init__(self, minifi_container, config_path, bin_path):
+        self.minifi_container = minifi_container
+        self.config_path = config_path
+        self.bin_path = bin_path
+
+    def set_controller_socket_properties(self):
+        self.minifi_container.properties["controller.socket.enable"] = "true"
+        self.minifi_container.properties["controller.socket.host"] = "localhost"
+        self.minifi_container.properties["controller.socket.port"] = "9998"
+        self.minifi_container.properties["controller.socket.local.any.interface"] = "false"
+
+    def update_flow_config_through_controller(self):
+        self.minifi_container.exec_run([f"{self.bin_path}/minifi-controller", "--updateflow", "/tmp/resources/minifi-controller/config.yml"])
+
+    def updated_config_is_persisted(self) -> bool:
+        exit_code, output = self.minifi_container.exec_run(["cat", self.config_path])
+        if exit_code != 0:
+            logging.error(f"Failed to read MiNiFi config file to check if updated config is persisted: {exit_code} {output}")
+            return False
+        return "2f2a3b47-f5ba-49f6-82b5-bc1c86b96f38" in output
+
+    def stop_component_through_controller(self, component: str):
+        self.minifi_container.exec_run([f"{self.bin_path}/minifi-controller", "--stop", component])
+
+    def start_component_through_controller(self, component: str):
+        self.minifi_container.exec_run([f"{self.bin_path}/minifi-controller", "--start", component])
+
+    @retry_check(max_tries=10, retry_interval_seconds=1)
+    def is_component_running(self, component: str) -> bool:
+        (code, output) = self.minifi_container.exec_run([f"{self.bin_path}/minifi-controller", "--list", "components"])
+        return code == 0 and component + ", running: true" in output
+
+    def get_connections(self):
+        (_, output) = self.minifi_container.exec_run([f"{self.bin_path}/minifi-controller", "--list", "connections"])
+        connections = []
+        for line in output.split('\n'):
+            if not line.startswith('[') and not line.startswith('Connection Names'):
+                connections.append(line)
+        return connections
+
+    @retry_check(max_tries=10, retry_interval_seconds=1)
+    def connection_found_through_controller(self, connection: str) -> bool:
+        return connection in self.get_connections()
+
+    def get_full_connection_count(self) -> int:
+        (_, output) = self.minifi_container.exec_run([f"{self.bin_path}/minifi-controller", "--getfull"])
+        for line in output.split('\n'):
+            if "are full" in line:
+                return int(line.split(' ')[0])
+        return -1
+
+    def get_connection_size(self, connection: str):
+        (_, output) = self.minifi_container.exec_run([f"{self.bin_path}/minifi-controller", "--getsize", connection])
+        for line in output.split('\n'):
+            if "Size/Max of " + connection in line:
+                size_and_max = line.split(connection)[1].split('/')
+                return (int(size_and_max[0].strip()), int(size_and_max[1].strip()))
+        return (-1, -1)
+
+    def get_manifest(self) -> str:
+        (_, output) = self.minifi_container.exec_run([f"{self.bin_path}/minifi-controller", "--manifest"])
+        manifest = ""
+        for line in output.split('\n'):
+            if not line.startswith('['):
+                manifest += line
+        return manifest
+
+    def create_debug_bundle(self) -> bool:
+        (code, _) = self.minifi_container.exec_run([f"{self.bin_path}/minifi-controller", "--debug", "/tmp"])
+        if code != 0:
+            logging.error("Minifi controller debug command failed with code: %d", code)
+            return False
+
+        (code, _) = self.minifi_container.exec_run(["test", "-f", "/tmp/debug.tar.gz"])
+        return code == 0
diff --git a/behave_framework/src/minifi_test_framework/containers/minifi_linux_container.py b/behave_framework/src/minifi_test_framework/containers/minifi_linux_container.py
new file mode 100644
index 000000000..62311d9ab
--- /dev/null
+++ b/behave_framework/src/minifi_test_framework/containers/minifi_linux_container.py
@@ -0,0 +1,146 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one or more
+#  contributor license agreements.  See the NOTICE file distributed with
+#  this work for additional information regarding copyright ownership.
+#  The ASF licenses this file to You under the Apache License, Version 2.0
+#  (the "License"); you may not use this file except in compliance with
+#  the License.  You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing, software
+#  distributed under the License is distributed on an "AS IS" BASIS,
+#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+#  See the License for the specific language governing permissions and
+#  limitations under the License.
+#
+
+import logging
+import os
+from minifi_test_framework.containers.file import File
+from minifi_test_framework.containers.host_file import HostFile
+from minifi_test_framework.core.helpers import wait_for_condition
+from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_test_framework.core.ssl_utils import make_cert_without_extended_usage, make_client_cert, make_server_cert, \
+    dump_cert, dump_key
+from minifi_test_framework.minifi.minifi_flow_definition import MinifiFlowDefinition
+from pathlib import Path
+
+from .container_linux import LinuxContainer
+from .minifi_controller import MinifiController
+from .minifi_protocol import MinifiProtocol
+
+
+class NormalDeployment:
+    def __init__(self):
+        self.conf_path = "/opt/minifi/minifi-current/conf"
+        self.bin_path = "/opt/minifi/minifi-current/bin"
+        self.extension_pattern = "../extensions/*"
+        self.minifi_python_path = "/opt/minifi/minifi-current/minifi-python"
+        self.minifi_python_examples_path = "/opt/minifi/minifi-current/minifi-python-examples"
+
+
+class FHSDeployment:
+    def __init__(self):
+        self.conf_path = "/etc/nifi-minifi-cpp/"
+        self.bin_path = "/usr/bin"
+        self.extension_pattern = "/usr/lib64/nifi-minifi-cpp/extensions/*"
+        self.minifi_python_path = "/var/lib/nifi-minifi-cpp/minifi-python"
+        self.minifi_python_examples_path = "/usr/share/doc/nifi-minifi-cpp/pythonprocessor-examples"
+
+
+class MinifiLinuxContainer(LinuxContainer, MinifiProtocol):
+    def __init__(self, container_name: str, test_context: MinifiTestContext,
+                 deployment: NormalDeployment | FHSDeployment):
+        super().__init__(test_context.minifi_container_image, f"{container_name}-{test_context.scenario_id}",
+                         test_context.network)
+        self.flow_definition = MinifiFlowDefinition()
+        self.properties: dict[str, str] = {}
+        self.log_properties: dict[str, str] = {}
+        self.scenario_id = test_context.scenario_id
+        self.deploy_timeout_seconds = 20
+        self.deployment_type = deployment
+
+        self.controller = MinifiController(self, f"{self.deployment_type.conf_path}/config.yml",
+                                           self.deployment_type.bin_path)
+
+        minifi_client_cert, minifi_client_key = make_cert_without_extended_usage(common_name=self.container_name,
+                                                                                 ca_cert=test_context.root_ca_cert,
+                                                                                 ca_key=test_context.root_ca_key)
+        self.files.append(File("/usr/local/share/certs/ca-root-nss.crt", dump_cert(test_context.root_ca_cert)))
+        self.files.append(File("/tmp/resources/root_ca.crt", dump_cert(test_context.root_ca_cert)))
+        self.files.append(File("/tmp/resources/minifi_client.crt", dump_cert(minifi_client_cert)))
+        self.files.append(File("/tmp/resources/minifi_client.key", dump_key(minifi_client_key)))
+        self.files.append(
+            File("/tmp/resources/minifi_merged_cert.crt", dump_cert(minifi_client_cert) + dump_key(minifi_client_key)))
+
+        clientuser_cert, clientuser_key = make_client_cert("clientuser", ca_cert=test_context.root_ca_cert,
+                                                           ca_key=test_context.root_ca_key)
+        self.files.append(File("/tmp/resources/clientuser.crt", dump_cert(clientuser_cert)))
+        self.files.append(File("/tmp/resources/clientuser.key", dump_key(clientuser_key)))
+
+        minifi_server_cert, minifi_server_key = make_server_cert(common_name=f"server-{test_context.scenario_id}",
+                                                                 ca_cert=test_context.root_ca_cert,
+                                                                 ca_key=test_context.root_ca_key)
+        self.files.append(
+            File("/tmp/resources/minifi_server.crt", dump_cert(cert=minifi_server_cert) + dump_key(minifi_server_key)))
+
+        self._fill_default_properties()
+        self._fill_default_log_properties()
+
+    def deploy(self, context: MinifiTestContext | None) -> bool:
+        flow_config = self.flow_definition.to_yaml()
+        logging.info(f"Deploying MiNiFi container '{self.container_name}' with flow configuration:\n{flow_config}")
+        self.files.append(File(f"{self.deployment_type.conf_path}/config.yml", flow_config))
+        self.files.append(
+            File(f"{self.deployment_type.conf_path}/minifi.properties", self._get_properties_file_content()))
+        self.files.append(
+            File(f"{self.deployment_type.conf_path}/minifi-log.properties", self._get_log_properties_file_content()))
+        resource_dir = Path(__file__).resolve().parent / "resources" / "minifi-controller"
+        self.host_files.append(
+            HostFile("/tmp/resources/minifi-controller/config.yml", os.path.join(resource_dir, "config.yml")))
+
+        if not super().deploy(context):
+            return False
+
+        finished_str = "MiNiFi started"
+        return wait_for_condition(condition=lambda: finished_str in self.get_logs(),
+                                  timeout_seconds=self.deploy_timeout_seconds, bail_condition=lambda: self.exited,
+                                  context=context)
+
+    def set_deploy_timeout_seconds(self, timeout_seconds: int):
+        self.deploy_timeout_seconds = timeout_seconds
+
+    def set_property(self, key: str, value: str):
+        self.properties[key] = value
+
+    def set_log_property(self, key: str, value: str):
+        self.log_properties[key] = value
+
+    def _fill_default_properties(self):
+        self.properties["nifi.flow.configuration.file"] = f"{self.deployment_type.conf_path}/config.yml"
+        self.properties["nifi.extension.path"] = self.deployment_type.extension_pattern
+        self.properties["nifi.administrative.yield.duration"] = "1 sec"
+        self.properties["nifi.bored.yield.duration"] = "100 millis"
+        self.properties["nifi.openssl.fips.support.enable"] = "false"
+        self.properties["nifi.provenance.repository.class.name"] = "NoOpRepository"
+        self.properties["nifi.python.processor.dir"] = self.deployment_type.minifi_python_path
+
+    def _fill_default_log_properties(self):
+        self.log_properties["spdlog.pattern"] = "[%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v"
+
+        self.log_properties["appender.stderr"] = "stderr"
+        self.log_properties["logger.root"] = "DEBUG, stderr"
+        self.log_properties["logger.org::apache::nifi::minifi"] = "DEBUG, stderr"
+
+    def _get_properties_file_content(self):
+        lines = (f"{key}={value}" for key, value in self.properties.items())
+        return "\n".join(lines)
+
+    def _get_log_properties_file_content(self):
+        lines = (f"{key}={value}" for key, value in self.log_properties.items())
+        return "\n".join(lines)
+
+    def add_example_python_processors(self):
+        run_minifi_cmd = f'{self.deployment_type.bin_path}/minifi.sh run'
+        self.command = f'sh -c "cp -r {self.deployment_type.minifi_python_examples_path} {self.deployment_type.minifi_python_path}/examples && {run_minifi_cmd}"'
diff --git a/behave_framework/src/minifi_test_framework/containers/minifi_protocol.py b/behave_framework/src/minifi_test_framework/containers/minifi_protocol.py
new file mode 100644
index 000000000..473797f1d
--- /dev/null
+++ b/behave_framework/src/minifi_test_framework/containers/minifi_protocol.py
@@ -0,0 +1,58 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from typing import Protocol
+from minifi_test_framework.minifi.flow_definition import FlowDefinition
+
+
+class MinifiProtocol(Protocol):
+    flow_definition: FlowDefinition
+
+    def set_property(self, key: str, value: str):
+        ...
+
+    def set_log_property(self, key: str, value: str):
+        ...
+
+    def set_deploy_timeout_seconds(self, timeout_seconds: int):
+        ...
+
+
+def set_controller_socket_properties(minifi: MinifiProtocol):
+    minifi.set_property("controller.socket.enable", "true")
+    minifi.set_property("controller.socket.host", "localhost")
+    minifi.set_property("controller.socket.port", "9998")
+    minifi.set_property("controller.socket.local.any.interface", "false")
+
+
+def enable_openssl_fips_mode(minifi: MinifiProtocol):
+    minifi.set_property("nifi.openssl.fips.support.enable", "true")
+
+
+def enable_log_metrics_publisher(minifi: MinifiProtocol):
+    minifi.set_property("nifi.metrics.publisher.LogMetricsPublisher.metrics", "RepositoryMetrics")
+    minifi.set_property("nifi.metrics.publisher.LogMetricsPublisher.logging.interval", "1s")
+    minifi.set_property("nifi.metrics.publisher.class", "LogMetricsPublisher")
+
+
+def configure_c2_flow_url(minifi: MinifiProtocol, scenario_id: str):
+    minifi.set_property("nifi.c2.flow.url", f"http://minifi-c2-server-{scenario_id}:10090/c2/config?class=minifi-test-class")
+
+
+def set_up_ssl_properties(minifi: MinifiProtocol):
+    minifi.set_property("nifi.remote.input.secure", "true")
+    minifi.set_property("nifi.security.client.certificate", "/tmp/resources/minifi_client.crt")
+    minifi.set_property("nifi.security.client.private.key", "/tmp/resources/minifi_client.key")
+    minifi.set_property("nifi.security.client.ca.certificate", "/tmp/resources/root_ca.crt")
diff --git a/behave_framework/src/minifi_test_framework/containers/minifi_win_container.py b/behave_framework/src/minifi_test_framework/containers/minifi_win_container.py
new file mode 100644
index 000000000..d39216bd3
--- /dev/null
+++ b/behave_framework/src/minifi_test_framework/containers/minifi_win_container.py
@@ -0,0 +1,75 @@
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from typing import Dict
+
+from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_test_framework.minifi.minifi_flow_definition import MinifiFlowDefinition
+from minifi_test_framework.containers.directory import Directory
+from .container_windows import WindowsContainer
+from .minifi_protocol import MinifiProtocol
+import logging
+
+
+class MinifiWindowsContainer(WindowsContainer, MinifiProtocol):
+    def __init__(self, container_name: str, test_context: MinifiTestContext):
+        super().__init__(test_context.minifi_container_image, f"{container_name}-{test_context.scenario_id}", test_context.network)
+        self.flow_config_str: str = ""
+        self.flow_definition = MinifiFlowDefinition()
+        self.properties: Dict[str, str] = {}
+        self.log_properties: Dict[str, str] = {}
+
+        self._fill_default_properties()
+        self._fill_default_log_properties()
+
+    def deploy(self, context: MinifiTestContext | None) -> bool:
+        logging.info(self.flow_definition.to_yaml())
+        conf_dir = Directory("\\Program Files\\ApacheNiFiMiNiFi\\nifi-minifi-cpp\\conf")
+        conf_dir.add_file("config.yml", self.flow_definition.to_yaml())
+        conf_dir.add_file("minifi.properties", self._get_properties_file_content())
+        conf_dir.add_file("minifi-log.properties", self._get_log_properties_file_content())
+
+        self.dirs.append(conf_dir)
+        return super().deploy(context)
+
+    def set_property(self, key: str, value: str):
+        self.properties[key] = value
+
+    def set_log_property(self, key: str, value: str):
+        self.log_properties[key] = value
+
+    def _fill_default_properties(self):
+        self.properties["nifi.flow.configuration.file"] = "./conf/config.yml"
+        self.properties["nifi.extension.path"] = "../extensions/*"
+        self.properties["nifi.administrative.yield.duration"] = "1 sec"
+        self.properties["nifi.bored.yield.duration"] = "100 millis"
+        self.properties["nifi.openssl.fips.support.enable"] = "false"
+        self.properties["nifi.provenance.repository.class.name"] = "NoOpRepository"
+
+    def _fill_default_log_properties(self):
+        self.log_properties["spdlog.pattern"] = "[%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v"
+
+        self.log_properties["appender.stderr"] = "stderr"
+        self.log_properties["logger.root"] = "DEBUG, stderr"
+        self.log_properties["logger.org::apache::nifi::minifi"] = "DEBUG, stderr"
+
+    def _get_properties_file_content(self):
+        lines = (f"{key}={value}" for key, value in self.properties.items())
+        return "\n".join(lines)
+
+    def _get_log_properties_file_content(self):
+        lines = (f"{key}={value}" for key, value in self.log_properties.items())
+        return "\n".join(lines)
diff --git a/behave_framework/src/minifi_test_framework/containers/nifi_container.py b/behave_framework/src/minifi_test_framework/containers/nifi_container.py
index ae0f9d0f8..3d589a8fe 100644
--- a/behave_framework/src/minifi_test_framework/containers/nifi_container.py
+++ b/behave_framework/src/minifi_test_framework/containers/nifi_container.py
@@ -18,18 +18,19 @@ import gzip
 import logging
 import os
 from pathlib import Path
-from OpenSSL import crypto
 
 from minifi_test_framework.containers.file import File
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.core.helpers import wait_for_condition
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 from minifi_test_framework.minifi.nifi_flow_definition import NifiFlowDefinition
 from minifi_test_framework.containers.host_file import HostFile
 from minifi_test_framework.core.ssl_utils import make_server_cert
 
+from minifi_test_framework.core.ssl_utils import dump_cert, dump_key
 
-class NifiContainer(Container):
+
+class NifiContainer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext, command: list[str] | None = None, use_ssl: bool = False):
         self.flow_definition = NifiFlowDefinition()
         name = f"nifi-{test_context.scenario_id}"
@@ -78,9 +79,9 @@ class NifiContainer(Container):
         self.host_files.append(HostFile("/scripts/convert_cert_to_jks.sh", os.path.join(resource_dir, "convert_cert_to_jks.sh")))
 
         nifi_client_cert, nifi_client_key = make_server_cert(common_name=f"nifi-{test_context.scenario_id}", ca_cert=test_context.root_ca_cert, ca_key=test_context.root_ca_key)
-        self.files.append(File("/tmp/resources/root_ca.crt", crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=test_context.root_ca_cert)))
-        self.files.append(File("/tmp/resources/nifi_client.crt", crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=nifi_client_cert)))
-        self.files.append(File("/tmp/resources/nifi_client.key", crypto.dump_privatekey(type=crypto.FILETYPE_PEM, pkey=nifi_client_key)))
+        self.files.append(File("/tmp/resources/root_ca.crt", dump_cert(test_context.root_ca_cert)))
+        self.files.append(File("/tmp/resources/nifi_client.crt", dump_cert(nifi_client_cert)))
+        self.files.append(File("/tmp/resources/nifi_client.key", dump_key(nifi_client_key)))
 
     def deploy(self, context: MinifiTestContext | None) -> bool:
         flow_config = self.flow_definition.to_json()
diff --git a/behave_framework/src/minifi_test_framework/core/hooks.py b/behave_framework/src/minifi_test_framework/core/hooks.py
index 3e4e69a95..ea440cc30 100644
--- a/behave_framework/src/minifi_test_framework/core/hooks.py
+++ b/behave_framework/src/minifi_test_framework/core/hooks.py
@@ -46,6 +46,10 @@ def inject_scenario_id(context: MinifiTestContext, step):
 
 
 def common_before_scenario(context: Context, scenario: Scenario):
+    if "SUPPORTS_WINDOWS" not in scenario.effective_tags and os.name == 'nt':
+        scenario.skip("No windows support")
+        return
+
     if not hasattr(context, "minifi_container_image"):
         context.minifi_container_image = get_minifi_container_image()
 
@@ -93,6 +97,8 @@ def common_after_scenario(context: MinifiTestContext, scenario: Scenario):
         with open(scenario_info_path, "w") as f:
             f.write(header)
 
-    for container in context.containers.values():
-        container.clean_up()
-    context.network.remove()
+    if hasattr(context, 'containers'):
+        for container in context.containers.values():
+            container.clean_up()
+    if hasattr(context, 'network'):
+        context.network.remove()
diff --git a/behave_framework/src/minifi_test_framework/core/minifi_test_context.py b/behave_framework/src/minifi_test_framework/core/minifi_test_context.py
index 959f2e1fa..85970b101 100644
--- a/behave_framework/src/minifi_test_framework/core/minifi_test_context.py
+++ b/behave_framework/src/minifi_test_framework/core/minifi_test_context.py
@@ -16,32 +16,46 @@
 #
 
 from __future__ import annotations
-from typing import TYPE_CHECKING
+
+import os
+import docker
 from behave.runner import Context
+from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
+from cryptography.x509 import Certificate
 from docker.models.networks import Network
 
-from minifi_test_framework.containers.container import Container
-from OpenSSL import crypto
+from minifi_test_framework.containers.container_protocol import ContainerProtocol
+from minifi_test_framework.containers.minifi_protocol import MinifiProtocol
 
-if TYPE_CHECKING:
-    from minifi_test_framework.containers.minifi_container import MinifiContainer
 
 DEFAULT_MINIFI_CONTAINER_NAME = "minifi-primary"
 
 
+class MinifiContainer(ContainerProtocol, MinifiProtocol):
+    pass
+
+
 class MinifiTestContext(Context):
-    containers: dict[str, Container]
+    containers: dict[str, ContainerProtocol]
     scenario_id: str
     network: Network
     minifi_container_image: str
     resource_dir: str | None
-    root_ca_key: crypto.PKey
-    root_ca_cert: crypto.X509
+    root_ca_key: RSAPrivateKey
+    root_ca_cert: Certificate
 
     def get_or_create_minifi_container(self, container_name: str) -> MinifiContainer:
         if container_name not in self.containers:
-            from minifi_test_framework.containers.minifi_container import MinifiContainer
-            self.containers[container_name] = MinifiContainer(container_name, self)
+            if os.name == 'nt':
+                from minifi_test_framework.containers.minifi_win_container import MinifiWindowsContainer
+                minifi_container = MinifiWindowsContainer(container_name, self)
+            elif 'MINIFI_INSTALLATION_TYPE=FHS' in str(docker.from_env().images.get(self.minifi_container_image).history()):
+                from minifi_test_framework.containers.minifi_linux_container import MinifiLinuxContainer, FHSDeployment
+                minifi_container = MinifiLinuxContainer(container_name, self, FHSDeployment())
+            else:
+                from minifi_test_framework.containers.minifi_linux_container import MinifiLinuxContainer, NormalDeployment
+                minifi_container = MinifiLinuxContainer(container_name, self, NormalDeployment())
+            self.containers[container_name] = minifi_container
         return self.containers[container_name]
 
     def get_or_create_default_minifi_container(self) -> MinifiContainer:
diff --git a/behave_framework/src/minifi_test_framework/core/ssl_utils.py b/behave_framework/src/minifi_test_framework/core/ssl_utils.py
index d39cf9646..7fe37d251 100644
--- a/behave_framework/src/minifi_test_framework/core/ssl_utils.py
+++ b/behave_framework/src/minifi_test_framework/core/ssl_utils.py
@@ -13,149 +13,79 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import datetime
 
-import time
-import logging
-import random
-
-from M2Crypto import X509, EVP, RSA, ASN1
-from OpenSSL import crypto
-
-
-def gen_cert():
-    """
-    Generate TLS certificate request for testing
-    """
-
-    req, key = gen_req()
-    pub_key = req.get_pubkey()
-    subject = req.get_subject()
-    cert = X509.X509()
-    # noinspection PyTypeChecker
-    cert.set_serial_number(1)
-    cert.set_version(2)
-    cert.set_subject(subject)
-    t = int(time.time())
-    now = ASN1.ASN1_UTCTIME()
-    now.set_time(t)
-    now_plus_year = ASN1.ASN1_UTCTIME()
-    now_plus_year.set_time(t + 60 * 60 * 24 * 365)
-    cert.set_not_before(now)
-    cert.set_not_after(now_plus_year)
-    issuer = X509.X509_Name()
-    issuer.C = 'US'
-    issuer.CN = 'minifi-listen'
-    cert.set_issuer(issuer)
-    cert.set_pubkey(pub_key)
-    cert.sign(key, 'sha256')
-
-    return cert, key
-
-
-def rsa_gen_key_callback():
-    pass
-
-
-def gen_req():
-    """
-    Generate TLS certificate request for testing
-    """
-
-    logging.info('Generating test certificate request')
-    key = EVP.PKey()
-    req = X509.Request()
-    rsa = RSA.gen_key(1024, 65537, rsa_gen_key_callback)
-    key.assign_rsa(rsa)
-    req.set_pubkey(key)
-    name = req.get_subject()
-    name.C = 'US'
-    name.CN = 'minifi-listen'
-    req.sign(key, 'sha256')
-
-    return req, key
+from cryptography import x509
+from cryptography.hazmat.primitives import hashes
+from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import rsa
+from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey
+from cryptography.x509 import Certificate, ExtendedKeyUsage
+from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
 
 
-def make_self_signed_cert(common_name):
-    ca_key = crypto.PKey()
-    ca_key.generate_key(crypto.TYPE_RSA, 2048)
+def gen_cert() -> tuple[Certificate, RSAPrivateKey]:
+    key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
 
-    ca_cert = crypto.X509()
-    ca_cert.set_version(2)
-    ca_cert.set_serial_number(random.randint(50000000, 100000000))
+    subject = issuer = x509.Name([x509.NameAttribute(NameOID.COUNTRY_NAME, u"US"), x509.NameAttribute(NameOID.COMMON_NAME, u"minifi-listen"), ])
 
-    ca_subj = ca_cert.get_subject()
-    ca_subj.commonName = common_name
+    cert = x509.CertificateBuilder().subject_name(subject).issuer_name(issuer).public_key(key.public_key()).serial_number(
+        x509.random_serial_number()).not_valid_before(datetime.datetime.now(datetime.timezone.utc)).not_valid_after(
+        datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=365)).sign(key, hashes.SHA256())
 
-    ca_cert.add_extensions([
-        crypto.X509Extension(b"subjectKeyIdentifier", False, b"hash", subject=ca_cert),
-    ])
-
-    ca_cert.add_extensions([
-        crypto.X509Extension(b"authorityKeyIdentifier", False, b"keyid:always", issuer=ca_cert),
-    ])
-
-    ca_cert.add_extensions([
-        crypto.X509Extension(b"basicConstraints", False, b"CA:TRUE"),
-        crypto.X509Extension(b"keyUsage", False, b"keyCertSign, cRLSign"),
-    ])
-
-    ca_cert.set_issuer(ca_subj)
-    ca_cert.set_pubkey(ca_key)
+    return cert, key
 
-    ca_cert.gmtime_adj_notBefore(0)
-    ca_cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60)
 
-    ca_cert.sign(ca_key, 'sha256')
+def make_self_signed_cert(common_name: str) -> tuple[Certificate, RSAPrivateKey]:
+    key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
 
-    return ca_cert, ca_key
+    subject = issuer = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name), ])
 
+    cert = x509.CertificateBuilder().subject_name(subject).issuer_name(issuer).public_key(key.public_key()).serial_number(
+        x509.random_serial_number()).not_valid_before(datetime.datetime.now(datetime.timezone.utc)).not_valid_after(
+        datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=3650)).add_extension(
+        x509.SubjectKeyIdentifier.from_public_key(key.public_key()), critical=False, ).add_extension(x509.BasicConstraints(ca=True, path_length=None),
+                                                                                                     critical=True, ).sign(key, hashes.SHA256())
 
-def _make_cert(common_name, ca_cert, ca_key, extended_key_usage=None):
-    key = crypto.PKey()
-    key.generate_key(crypto.TYPE_RSA, 2048)
+    return cert, key
 
-    cert = crypto.X509()
-    cert.set_version(2)
-    cert.set_serial_number(random.randint(50000000, 100000000))
 
-    client_subj = cert.get_subject()
-    client_subj.commonName = common_name
+def _make_cert(common_name: str, ca_cert: Certificate, ca_key: RSAPrivateKey, extended_key_usage: ExtendedKeyUsage | None) -> tuple[
+        Certificate, RSAPrivateKey]:
+    key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
 
-    cert.add_extensions([
-        crypto.X509Extension(b"basicConstraints", False, b"CA:FALSE"),
-        crypto.X509Extension(b"subjectKeyIdentifier", False, b"hash", subject=cert),
-    ])
+    subject = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, common_name), ])
 
-    extensions = [crypto.X509Extension(b"authorityKeyIdentifier", False, b"keyid:always", issuer=ca_cert),
-                  crypto.X509Extension(b"keyUsage", False, b"digitalSignature")]
+    builder = x509.CertificateBuilder().subject_name(subject).issuer_name(ca_cert.subject).public_key(key.public_key()).serial_number(
+        x509.random_serial_number()).not_valid_before(datetime.datetime.now(datetime.timezone.utc)).not_valid_after(
+        datetime.datetime.now(datetime.timezone.utc) + datetime.timedelta(days=3650)).add_extension(x509.BasicConstraints(ca=False, path_length=None),
+                                                                                                    critical=True, ).add_extension(
+        x509.SubjectKeyIdentifier.from_public_key(key.public_key()), critical=False, ).add_extension(
+        x509.SubjectAlternativeName([x509.DNSName(common_name)]), critical=False, )
 
     if extended_key_usage:
-        extensions.append(crypto.X509Extension(b"extendedKeyUsage", False, extended_key_usage))
-
-    cert.add_extensions([
-        crypto.X509Extension(b"subjectAltName", False, b"DNS.1:" + common_name.encode())
-    ])
+        builder = builder.add_extension(x509.ExtendedKeyUsage(extended_key_usage), critical=False)
 
-    cert.add_extensions(extensions)
+    cert = builder.sign(ca_key, hashes.SHA256())
+    return cert, key
 
-    cert.set_issuer(ca_cert.get_subject())
-    cert.set_pubkey(key)
 
-    cert.gmtime_adj_notBefore(0)
-    cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60)
+def make_client_cert(common_name: str, ca_cert: Certificate, ca_key: RSAPrivateKey) -> tuple[Certificate, RSAPrivateKey]:
+    return _make_cert(common_name, ca_cert, ca_key, x509.ExtendedKeyUsage([ExtendedKeyUsageOID.CLIENT_AUTH]))
 
-    cert.sign(ca_key, 'sha256')
 
-    return cert, key
+def make_server_cert(common_name: str, ca_cert: Certificate, ca_key: RSAPrivateKey) -> tuple[Certificate, RSAPrivateKey]:
+    return _make_cert(common_name, ca_cert, ca_key, x509.ExtendedKeyUsage([ExtendedKeyUsageOID.SERVER_AUTH]))
 
 
-def make_client_cert(common_name, ca_cert, ca_key):
-    return _make_cert(common_name=common_name, ca_cert=ca_cert, ca_key=ca_key, extended_key_usage=b"clientAuth")
+def make_cert_without_extended_usage(common_name: str, ca_cert: Certificate, ca_key: RSAPrivateKey) -> tuple[Certificate, RSAPrivateKey]:
+    return _make_cert(common_name, ca_cert, ca_key, None)
 
 
-def make_server_cert(common_name, ca_cert, ca_key):
-    return _make_cert(common_name=common_name, ca_cert=ca_cert, ca_key=ca_key, extended_key_usage=b"serverAuth")
+def dump_cert(cert: Certificate, encoding_type: serialization.Encoding = serialization.Encoding.PEM) -> bytes:
+    return cert.public_bytes(encoding_type)
 
 
-def make_cert_without_extended_usage(common_name, ca_cert, ca_key):
-    return _make_cert(common_name=common_name, ca_cert=ca_cert, ca_key=ca_key, extended_key_usage=None)
+def dump_key(key: RSAPrivateKey, encoding_type: serialization.Encoding = serialization.Encoding.PEM) -> bytes:
+    return key.private_bytes(encoding=encoding_type, format=serialization.PrivateFormat.TraditionalOpenSSL,
+                             encryption_algorithm=serialization.NoEncryption())
diff --git a/behave_framework/src/minifi_test_framework/steps/configuration_steps.py b/behave_framework/src/minifi_test_framework/steps/configuration_steps.py
index 714915558..7c2dd715c 100644
--- a/behave_framework/src/minifi_test_framework/steps/configuration_steps.py
+++ b/behave_framework/src/minifi_test_framework/steps/configuration_steps.py
@@ -18,6 +18,10 @@
 from behave import step, given
 
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
+from minifi_test_framework.containers.minifi_protocol import enable_openssl_fips_mode
+from minifi_test_framework.containers.minifi_protocol import set_up_ssl_properties
+from minifi_test_framework.containers.minifi_protocol import enable_log_metrics_publisher
+from minifi_test_framework.containers.minifi_protocol import configure_c2_flow_url
 
 
 @step('MiNiFi configuration "{config_key}" is set to "{config_value}"')
@@ -27,7 +31,7 @@ def set_minifi_config_property(context: MinifiTestContext, config_key: str, conf
 
 @step("log metrics publisher is enabled in MiNiFi")
 def enable_minifi_log_metrics_publisher(context: MinifiTestContext):
-    context.get_or_create_default_minifi_container().enable_log_metrics_publisher()
+    enable_log_metrics_publisher(context.get_or_create_default_minifi_container())
 
 
 @step('log property "{log_property_key}" is set to "{log_property_value}"')
@@ -37,14 +41,14 @@ def set_minifi_log_property(context: MinifiTestContext, log_property_key: str, l
 
 @given("OpenSSL FIPS mode is enabled in MiNiFi")
 def enable_minifi_openssl_fips_mode(context: MinifiTestContext):
-    context.get_or_create_default_minifi_container().enable_openssl_fips_mode()
+    enable_openssl_fips_mode(context.get_or_create_default_minifi_container())
 
 
 @given("the C2 flow URL property is configured")
 def configure_c2_flow_url(context: MinifiTestContext):
-    context.get_or_create_default_minifi_container().configure_c2_flow_url()
+    configure_c2_flow_url(context.get_or_create_default_minifi_container(), context.scenario_id)
 
 
 @given("SSL properties are set in MiNiFi")
 def set_minifi_ssl_properties(context: MinifiTestContext):
-    context.get_or_create_default_minifi_container().set_up_ssl_properties()
+    set_up_ssl_properties(context.get_or_create_default_minifi_container())
diff --git a/docker/RunBehaveTests.sh b/docker/RunBehaveTests.sh
index bcfe34640..6492921e2 100755
--- a/docker/RunBehaveTests.sh
+++ b/docker/RunBehaveTests.sh
@@ -155,22 +155,6 @@ pip install --trusted-host pypi.python.org --upgrade pip setuptools
 # Install test dependencies
 echo "Installing test dependencies..." 1>&2
 
-# hint include/library paths if homewbrew is in use
-if brew list 2> /dev/null | grep openssl > /dev/null 2>&1; then
-  echo "Using homebrew paths for openssl" 1>&2
-  LDFLAGS="-L$(brew --prefix openssl@1.1)/lib"
-  export LDFLAGS
-  CFLAGS="-I$(brew --prefix openssl@1.1)/include"
-  export CFLAGS
-  SWIG_FEATURES="-cpperraswarn -includeall -I$(brew --prefix openssl@1.1)/include"
-  export SWIG_FEATURES
-fi
-
-if ! command swig -version &> /dev/null; then
-  echo "Swig could not be found on your system (dependency of m2crypto python library). Please install swig to continue."
-  exit 1
-fi
-
 pip install -e "${docker_dir}/../behave_framework"
 
 export TMPDIR="/tmp/behavex_ci_${RANDOM}"
@@ -192,6 +176,9 @@ fi
 
 echo "${BEHAVE_OPTS[@]}"
 
-mapfile -t FEATURE_FILES < <(find "${docker_dir}/../extensions" -type f -name '*.feature')
+FEATURE_FILES=()
+while IFS= read -r -d '' file; do
+    FEATURE_FILES+=("$file")
+done < <(find "${docker_dir}/../extensions" -type f -name '*.feature' -print0)
 
 behavex "${BEHAVE_OPTS[@]}" "${FEATURE_FILES[@]}"
diff --git a/docker/installed/win.Dockerfile b/docker/installed/win.Dockerfile
new file mode 100644
index 000000000..bce347ff1
--- /dev/null
+++ b/docker/installed/win.Dockerfile
@@ -0,0 +1,42 @@
+#escape=`
+
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+FROM mcr.microsoft.com/windows/servercore:ltsc2022
+
+LABEL maintainer="Apache NiFi <de...@nifi.apache.org>"
+
+ARG MSI_SOURCE="nifi-minifi-cpp.msi"
+
+ENV MINIFI_HOME="C:\Program Files\ApacheNiFiMiNiFi\nifi-minifi-cpp"
+
+SHELL ["powershell", "-Command", "$ErrorActionPreference = 'Stop'; $ProgressPreference = 'SilentlyContinue';"]
+
+RUN Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
+
+RUN choco install git -y
+RUN choco install vcredist140 -y
+
+COPY ${MSI_SOURCE} C:\temp\minifi.msi
+
+SHELL ["cmd", "/S", "/C"]
+
+RUN C:\Windows\System32\msiexec.exe /i C:\temp\minifi.msi /qn /norestart /L*V C:\minifi_install.log
+
+CMD ["C:\\Program Files\\ApacheNiFiMiNiFi\\nifi-minifi-cpp\\bin\\minifi.exe"]
\ No newline at end of file
diff --git a/extensions/aws/tests/features/containers/kinesis_server_container.py b/extensions/aws/tests/features/containers/kinesis_server_container.py
index 01b6dced2..d97bb5c83 100644
--- a/extensions/aws/tests/features/containers/kinesis_server_container.py
+++ b/extensions/aws/tests/features/containers/kinesis_server_container.py
@@ -16,13 +16,13 @@
 import logging
 
 from pathlib import Path
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.core.helpers import wait_for_condition, retry_check
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
 
 
-class KinesisServerContainer(Container):
+class KinesisServerContainer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext):
         builder = DockerImageBuilder(
             image_tag="minifi-kinesis-mock:latest",
diff --git a/extensions/aws/tests/features/containers/s3_server_container.py b/extensions/aws/tests/features/containers/s3_server_container.py
index 8f00b2644..2364b2d0c 100644
--- a/extensions/aws/tests/features/containers/s3_server_container.py
+++ b/extensions/aws/tests/features/containers/s3_server_container.py
@@ -16,12 +16,12 @@
 import json
 import logging
 
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.core.helpers import wait_for_condition
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 
 
-class S3ServerContainer(Container):
+class S3ServerContainer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext):
         super().__init__("adobe/s3mock:3.12.0", f"s3-server-{test_context.scenario_id}", test_context.network)
         self.environment.append("initialBuckets=test_bucket")
diff --git a/extensions/azure/tests/features/containers/azure_server_container.py b/extensions/azure/tests/features/containers/azure_server_container.py
index 4caeb786b..cb0d2e5eb 100644
--- a/extensions/azure/tests/features/containers/azure_server_container.py
+++ b/extensions/azure/tests/features/containers/azure_server_container.py
@@ -18,13 +18,13 @@
 import logging
 
 from docker.errors import ContainerError
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.core.helpers import run_cmd_in_docker_image
 from minifi_test_framework.core.helpers import wait_for_condition
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 
 
-class AzureServerContainer(Container):
+class AzureServerContainer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext):
         super().__init__("mcr.microsoft.com/azure-storage/azurite:3.35.0",
                          f"azure-storage-server-{test_context.scenario_id}",
diff --git a/extensions/couchbase/tests/features/containers/couchbase_server_container.py b/extensions/couchbase/tests/features/containers/couchbase_server_container.py
index f1cce4991..3f4f1401f 100644
--- a/extensions/couchbase/tests/features/containers/couchbase_server_container.py
+++ b/extensions/couchbase/tests/features/containers/couchbase_server_container.py
@@ -15,26 +15,25 @@
 
 import logging
 
-from OpenSSL import crypto
 from minifi_test_framework.core.helpers import wait_for_condition, retry_check
-from minifi_test_framework.core.ssl_utils import make_server_cert
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.core.ssl_utils import make_server_cert, dump_cert, dump_key
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.containers.file import File
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 from docker.errors import ContainerError
 
 
-class CouchbaseServerContainer(Container):
+class CouchbaseServerContainer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext):
         super().__init__("couchbase:enterprise-7.2.5", f"couchbase-server-{test_context.scenario_id}", test_context.network)
 
         couchbase_cert, couchbase_key = make_server_cert(self.container_name, test_context.root_ca_cert, test_context.root_ca_key)
 
-        root_ca_content = crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=test_context.root_ca_cert)
+        root_ca_content = dump_cert(test_context.root_ca_cert)
         self.files.append(File("/opt/couchbase/var/lib/couchbase/inbox/CA/root_ca.crt", root_ca_content, permissions=0o666))
-        couchbase_cert_content = crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=couchbase_cert)
+        couchbase_cert_content = dump_cert(couchbase_cert)
         self.files.append(File("/opt/couchbase/var/lib/couchbase/inbox/chain.pem", couchbase_cert_content, permissions=0o666))
-        couchbase_key_content = crypto.dump_privatekey(type=crypto.FILETYPE_PEM, pkey=couchbase_key)
+        couchbase_key_content = dump_key(couchbase_key)
         self.files.append(File("/opt/couchbase/var/lib/couchbase/inbox/pkey.key", couchbase_key_content, permissions=0o666))
 
     def deploy(self, context: MinifiTestContext | None) -> bool:
@@ -66,7 +65,7 @@ class CouchbaseServerContainer(Container):
 
         return True
 
-    @retry_check(max_tries=12, retry_interval=5)
+    @retry_check(max_tries=12, retry_interval_seconds=5)
     def _run_couchbase_cli_command(self, command):
         (code, output) = self.exec_run(command)
         if code != 0:
@@ -90,7 +89,7 @@ class CouchbaseServerContainer(Container):
             logging.error(f"Unexpected error while running python command '{command}' in couchbase helper docker: '{e}'")
             return False
 
-    @retry_check(max_tries=15, retry_interval=2)
+    @retry_check(max_tries=15, retry_interval_seconds=2)
     def _load_couchbase_certs(self):
         python_command = f"""
 import requests
diff --git a/extensions/elasticsearch/tests/features/containers/elastic_base_container.py b/extensions/elasticsearch/tests/features/containers/elastic_base_container.py
index 5db6e61dc..2287278c4 100644
--- a/extensions/elasticsearch/tests/features/containers/elastic_base_container.py
+++ b/extensions/elasticsearch/tests/features/containers/elastic_base_container.py
@@ -14,11 +14,11 @@
 # limitations under the License.
 
 from minifi_test_framework.core.helpers import wait_for_condition, retry_check
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 
 
-class ElasticBaseContainer(Container):
+class ElasticBaseContainer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext, image: str, container_name: str):
         super().__init__(image, container_name, test_context.network)
         self.user = None
diff --git a/extensions/elasticsearch/tests/features/containers/elasticsearch_container.py b/extensions/elasticsearch/tests/features/containers/elasticsearch_container.py
index 037711d75..aec928026 100644
--- a/extensions/elasticsearch/tests/features/containers/elasticsearch_container.py
+++ b/extensions/elasticsearch/tests/features/containers/elasticsearch_container.py
@@ -19,8 +19,7 @@ import os
 
 from elastic_base_container import ElasticBaseContainer
 from pathlib import Path
-from OpenSSL import crypto
-from minifi_test_framework.core.ssl_utils import make_server_cert, make_cert_without_extended_usage
+from minifi_test_framework.core.ssl_utils import make_server_cert, make_cert_without_extended_usage, dump_cert, dump_key
 from minifi_test_framework.containers.file import File
 from minifi_test_framework.containers.host_file import HostFile
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
@@ -33,19 +32,19 @@ class ElasticsearchContainer(ElasticBaseContainer):
         http_cert, http_key = make_server_cert(self.container_name, test_context.root_ca_cert, test_context.root_ca_key)
         transport_cert, transport_key = make_cert_without_extended_usage(self.container_name, test_context.root_ca_cert, test_context.root_ca_key)
 
-        root_ca_content = crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=test_context.root_ca_cert)
+        root_ca_content = dump_cert(test_context.root_ca_cert)
         self.files.append(File("/usr/share/elasticsearch/config/certs/root_ca.crt", root_ca_content, permissions=0o644))
 
-        http_cert_content = crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=http_cert)
+        http_cert_content = dump_cert(http_cert)
         self.files.append(File("/usr/share/elasticsearch/config/certs/elastic_http.crt", http_cert_content, permissions=0o644))
 
-        http_key_content = crypto.dump_privatekey(type=crypto.FILETYPE_PEM, pkey=http_key)
+        http_key_content = dump_key(http_key)
         self.files.append(File("/usr/share/elasticsearch/config/certs/elastic_http.key", http_key_content, permissions=0o644))
 
-        transport_cert_content = crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=transport_cert)
+        transport_cert_content = dump_cert(transport_cert)
         self.files.append(File("/usr/share/elasticsearch/config/certs/elastic_transport.crt", transport_cert_content, permissions=0o644))
 
-        transport_key_content = crypto.dump_privatekey(type=crypto.FILETYPE_PEM, pkey=transport_key)
+        transport_key_content = dump_key(transport_key)
         self.files.append(File("/usr/share/elasticsearch/config/certs/elastic_transport.key", transport_key_content, permissions=0o644))
 
         features_dir = Path(__file__).resolve().parent.parent
diff --git a/extensions/elasticsearch/tests/features/containers/opensearch_container.py b/extensions/elasticsearch/tests/features/containers/opensearch_container.py
index cdf5008d5..234a26d12 100644
--- a/extensions/elasticsearch/tests/features/containers/opensearch_container.py
+++ b/extensions/elasticsearch/tests/features/containers/opensearch_container.py
@@ -18,8 +18,7 @@ import logging
 
 from elastic_base_container import ElasticBaseContainer
 from pathlib import Path
-from OpenSSL import crypto
-from minifi_test_framework.core.ssl_utils import make_server_cert
+from minifi_test_framework.core.ssl_utils import make_server_cert, dump_cert, dump_key
 from minifi_test_framework.containers.file import File
 from minifi_test_framework.containers.host_file import HostFile
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
@@ -31,13 +30,13 @@ class OpensearchContainer(ElasticBaseContainer):
 
         admin_pem, admin_key = make_server_cert(self.container_name, test_context.root_ca_cert, test_context.root_ca_key)
 
-        root_ca_content = crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=test_context.root_ca_cert)
+        root_ca_content = dump_cert(test_context.root_ca_cert)
         self.files.append(File("/usr/share/opensearch/config/root-ca.pem", root_ca_content, permissions=0o644))
 
-        admin_pem_content = crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=admin_pem)
+        admin_pem_content = dump_cert(admin_pem)
         self.files.append(File("/usr/share/opensearch/config/admin.pem", admin_pem_content, permissions=0o644))
 
-        admin_key_content = crypto.dump_privatekey(type=crypto.FILETYPE_PEM, pkey=admin_key)
+        admin_key_content = dump_key(admin_key)
         self.files.append(File("/usr/share/opensearch/config/admin-key.pem", admin_key_content, permissions=0o644))
 
         features_dir = Path(__file__).resolve().parent.parent
diff --git a/extensions/gcp/tests/features/containers/fake_gcs_server_container.py b/extensions/gcp/tests/features/containers/fake_gcs_server_container.py
index a9733ba4e..5689065bb 100644
--- a/extensions/gcp/tests/features/containers/fake_gcs_server_container.py
+++ b/extensions/gcp/tests/features/containers/fake_gcs_server_container.py
@@ -15,12 +15,12 @@
 
 import logging
 from minifi_test_framework.core.helpers import wait_for_condition, retry_check
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.containers.directory import Directory
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 
 
-class FakeGcsServerContainer(Container):
+class FakeGcsServerContainer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext):
         super().__init__("fsouza/fake-gcs-server:1.45.1", f"fake-gcs-server-{test_context.scenario_id}", test_context.network,
                          command=f'-scheme http -host fake-gcs-server-{test_context.scenario_id}')
diff --git a/extensions/grafana-loki/tests/features/containers/grafana_loki_container.py b/extensions/grafana-loki/tests/features/containers/grafana_loki_container.py
index 32f1cf1db..af296c597 100644
--- a/extensions/grafana-loki/tests/features/containers/grafana_loki_container.py
+++ b/extensions/grafana-loki/tests/features/containers/grafana_loki_container.py
@@ -14,13 +14,12 @@
 # limitations under the License.
 
 import logging
-from OpenSSL import crypto
 
 from minifi_test_framework.core.helpers import wait_for_condition, retry_check
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.containers.file import File
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.core.ssl_utils import make_server_cert
+from minifi_test_framework.core.ssl_utils import make_server_cert, dump_cert, dump_key
 from docker.errors import ContainerError
 
 
@@ -30,20 +29,20 @@ class GrafanaLokiOptions:
         self.enable_multi_tenancy = enable_multi_tenancy
 
 
-class GrafanaLokiContainer(Container):
+class GrafanaLokiContainer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext, options: GrafanaLokiOptions):
         super().__init__("grafana/loki:3.2.1", f"grafana-loki-server-{test_context.scenario_id}", test_context.network)
         extra_ssl_settings = ""
         if options.enable_ssl:
             grafana_loki_cert, grafana_loki_key = make_server_cert(self.container_name, test_context.root_ca_cert, test_context.root_ca_key)
 
-            root_ca_content = crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=test_context.root_ca_cert)
+            root_ca_content = dump_cert(test_context.root_ca_cert)
             self.files.append(File("/etc/loki/root_ca.crt", root_ca_content, permissions=0o644))
 
-            grafana_loki_cert_content = crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=grafana_loki_cert)
+            grafana_loki_cert_content = dump_cert(grafana_loki_cert)
             self.files.append(File("/etc/loki/cert.pem", grafana_loki_cert_content, permissions=0o644))
 
-            grafana_loki_key_content = crypto.dump_privatekey(type=crypto.FILETYPE_PEM, pkey=grafana_loki_key)
+            grafana_loki_key_content = dump_key(grafana_loki_key)
             self.files.append(File("/etc/loki/key.pem", grafana_loki_key_content, permissions=0o644))
 
             extra_ssl_settings = """
diff --git a/extensions/grafana-loki/tests/features/containers/reverse_proxy_container.py b/extensions/grafana-loki/tests/features/containers/reverse_proxy_container.py
index 409a9b9e6..43efd49c5 100644
--- a/extensions/grafana-loki/tests/features/containers/reverse_proxy_container.py
+++ b/extensions/grafana-loki/tests/features/containers/reverse_proxy_container.py
@@ -14,11 +14,11 @@
 # limitations under the License.
 
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.core.helpers import wait_for_condition
 
 
-class ReverseProxyContainer(Container):
+class ReverseProxyContainer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext):
         super().__init__("minifi-reverse-proxy:latest", f"reverse-proxy-{test_context.scenario_id}", test_context.network)
         self.environment = [
diff --git a/extensions/kafka/tests/features/containers/kafka_server_container.py b/extensions/kafka/tests/features/containers/kafka_server_container.py
index 30073186f..09e35e131 100644
--- a/extensions/kafka/tests/features/containers/kafka_server_container.py
+++ b/extensions/kafka/tests/features/containers/kafka_server_container.py
@@ -15,17 +15,18 @@
 
 import logging
 import re
+
+from cryptography.hazmat.primitives import serialization
 import jks
 
-from OpenSSL import crypto
 from minifi_test_framework.core.helpers import wait_for_condition
-from minifi_test_framework.core.ssl_utils import make_server_cert
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.core.ssl_utils import make_server_cert, dump_cert, dump_key
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.containers.file import File
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 
 
-class KafkaServer(Container):
+class KafkaServer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext):
         super().__init__("apache/kafka:4.1.0", f"kafka-server-{test_context.scenario_id}", test_context.network)
         self.environment.append("KAFKA_NODE_ID=1")
@@ -55,7 +56,7 @@ class KafkaServer(Container):
 
         kafka_cert, kafka_key = make_server_cert(self.container_name, test_context.root_ca_cert, test_context.root_ca_key)
 
-        pke = jks.PrivateKeyEntry.new('kafka-broker-cert', [crypto.dump_certificate(crypto.FILETYPE_ASN1, kafka_cert)], crypto.dump_privatekey(crypto.FILETYPE_ASN1, kafka_key), 'rsa_raw')
+        pke = jks.PrivateKeyEntry.new('kafka-broker-cert', [dump_cert(kafka_cert, encoding_type=serialization.Encoding.DER)], dump_key(kafka_key, encoding_type=serialization.Encoding.DER), 'rsa_raw')
         server_keystore = jks.KeyStore.new('jks', [pke])
         server_keystore_content = server_keystore.saves('abcdefgh')
         self.files.append(File("/etc/kafka/secrets/kafka.keystore.jks", server_keystore_content, permissions=0o644))
@@ -63,7 +64,7 @@ class KafkaServer(Container):
 
         trusted_cert = jks.TrustedCertEntry.new(
             'root-ca',  # Alias for the certificate
-            crypto.dump_certificate(crypto.FILETYPE_ASN1, test_context.root_ca_cert)
+            dump_cert(test_context.root_ca_cert, encoding_type=serialization.Encoding.DER)
         )
 
         # Create a JKS keystore that will serve as a truststore with the trusted cert entry.
diff --git a/extensions/mqtt/tests/features/containers/mqtt_broker_container.py b/extensions/mqtt/tests/features/containers/mqtt_broker_container.py
index b309ff309..c4f4b7416 100644
--- a/extensions/mqtt/tests/features/containers/mqtt_broker_container.py
+++ b/extensions/mqtt/tests/features/containers/mqtt_broker_container.py
@@ -17,14 +17,14 @@ import logging
 import re
 from textwrap import dedent
 
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.core.helpers import wait_for_condition
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
 from docker.errors import ContainerError
 
 
-class MqttBrokerContainer(Container):
+class MqttBrokerContainer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext):
         dockerfile = dedent("""\
             FROM {base_image}
diff --git a/extensions/opc/tests/features/containers/opc_ua_server_container.py b/extensions/opc/tests/features/containers/opc_ua_server_container.py
index 3dfb4626d..3740b77bd 100644
--- a/extensions/opc/tests/features/containers/opc_ua_server_container.py
+++ b/extensions/opc/tests/features/containers/opc_ua_server_container.py
@@ -14,12 +14,12 @@
 # limitations under the License.
 
 from typing import List, Optional
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.core.helpers import wait_for_condition
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 
 
-class OPCUAServerContainer(Container):
+class OPCUAServerContainer(LinuxContainer):
     OPC_SERVER_IMAGE = "lordgamez/open62541:1.5.2"
 
     def __init__(self, test_context: MinifiTestContext, command: Optional[List[str]] = None):
diff --git a/extensions/prometheus/tests/features/containers/prometheus_container.py b/extensions/prometheus/tests/features/containers/prometheus_container.py
index 09053693e..829bd892e 100644
--- a/extensions/prometheus/tests/features/containers/prometheus_container.py
+++ b/extensions/prometheus/tests/features/containers/prometheus_container.py
@@ -14,15 +14,15 @@
 # limitations under the License.
 
 import logging
-from OpenSSL import crypto
-from minifi_test_framework.containers.container import Container
 from minifi_test_framework.core.helpers import wait_for_condition
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 from minifi_test_framework.core.ssl_utils import make_cert_without_extended_usage
 from minifi_test_framework.containers.file import File
+from minifi_test_framework.core.ssl_utils import dump_cert, dump_key
+from minifi_test_framework.containers.container_linux import LinuxContainer
 
 
-class PrometheusContainer(Container):
+class PrometheusContainer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext, ssl: bool = False):
         super().__init__("prom/prometheus:v3.9.1", f"prometheus-{test_context.scenario_id}", test_context.network)
         self.scenario_id = test_context.scenario_id
@@ -30,13 +30,13 @@ class PrometheusContainer(Container):
         if ssl:
             prometheus_cert, prometheus_key = make_cert_without_extended_usage(self.container_name, test_context.root_ca_cert, test_context.root_ca_key)
 
-            root_ca_content = crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=test_context.root_ca_cert)
+            root_ca_content = dump_cert(test_context.root_ca_cert)
             self.files.append(File("/etc/prometheus/certs/root-ca.pem", root_ca_content, permissions=0o644))
 
-            prometheus_cert_content = crypto.dump_certificate(type=crypto.FILETYPE_PEM, cert=prometheus_cert)
+            prometheus_cert_content = dump_cert(prometheus_cert)
             self.files.append(File("/etc/prometheus/certs/prometheus.crt", prometheus_cert_content, permissions=0o644))
 
-            prometheus_key_content = crypto.dump_privatekey(type=crypto.FILETYPE_PEM, pkey=prometheus_key)
+            prometheus_key_content = dump_key(prometheus_key)
             self.files.append(File("/etc/prometheus/certs/prometheus.key", prometheus_key_content, permissions=0o644))
 
             extra_ssl_settings = """
diff --git a/extensions/splunk/tests/features/containers/splunk_container.py b/extensions/splunk/tests/features/containers/splunk_container.py
index 4106bb63e..9a1488e63 100644
--- a/extensions/splunk/tests/features/containers/splunk_container.py
+++ b/extensions/splunk/tests/features/containers/splunk_container.py
@@ -15,15 +15,14 @@
 
 import json
 
-from OpenSSL import crypto
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 from minifi_test_framework.core.helpers import wait_for_condition, retry_check
 from minifi_test_framework.containers.file import File
-from minifi_test_framework.core.ssl_utils import make_server_cert
+from minifi_test_framework.core.ssl_utils import make_server_cert, dump_cert, dump_key
 
 
-class SplunkContainer(Container):
+class SplunkContainer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext):
         super().__init__("splunk/splunk:9.2.1-patch2", f"splunk-{test_context.scenario_id}", test_context.network)
         self.user = None
@@ -43,9 +42,9 @@ splunk:
         self.files.append(File("/tmp/defaults/default.yml", splunk_config_content, mode="rw", permissions=0o644))
 
         splunk_cert, splunk_key = make_server_cert(self.container_name, test_context.root_ca_cert, test_context.root_ca_key)
-        splunk_cert_content = crypto.dump_certificate(crypto.FILETYPE_PEM, splunk_cert)
-        splunk_key_content = crypto.dump_privatekey(crypto.FILETYPE_PEM, splunk_key)
-        root_ca_content = crypto.dump_certificate(crypto.FILETYPE_PEM, test_context.root_ca_cert)
+        splunk_cert_content = dump_cert(splunk_cert)
+        splunk_key_content = dump_key(splunk_key)
+        root_ca_content = dump_cert(test_context.root_ca_cert)
         self.files.append(File("/opt/splunk/etc/auth/splunk_cert.pem", splunk_cert_content.decode() + splunk_key_content.decode() + root_ca_content.decode(), permissions=0o644))
         self.files.append(File("/opt/splunk/etc/auth/root_ca.pem", root_ca_content.decode(), permissions=0o644))
 
diff --git a/extensions/sql/tests/features/containers/postgress_server_container.py b/extensions/sql/tests/features/containers/postgress_server_container.py
index f1df20e4b..2dea3058e 100644
--- a/extensions/sql/tests/features/containers/postgress_server_container.py
+++ b/extensions/sql/tests/features/containers/postgress_server_container.py
@@ -17,13 +17,13 @@
 
 import logging
 from textwrap import dedent
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
 from minifi_test_framework.core.helpers import wait_for_condition
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 
 
-class PostgresContainer(Container):
+class PostgresContainer(LinuxContainer):
     def __init__(self, context):
         dockerfile = dedent("""\
                 FROM {base_image}
diff --git a/extensions/standard-processors/tests/features/containers/diag_slave_container.py b/extensions/standard-processors/tests/features/containers/diag_slave_container.py
index 27b425958..4741441c4 100644
--- a/extensions/standard-processors/tests/features/containers/diag_slave_container.py
+++ b/extensions/standard-processors/tests/features/containers/diag_slave_container.py
@@ -18,13 +18,13 @@
 import logging
 from textwrap import dedent
 
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.containers.docker_image_builder import DockerImageBuilder
 from minifi_test_framework.core.helpers import wait_for_condition
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 
 
-class DiagSlave(Container):
+class DiagSlave(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext):
         dockerfile = dedent("""\
 FROM panterdsd/diagslave:latest
diff --git a/extensions/standard-processors/tests/features/containers/syslog_container.py b/extensions/standard-processors/tests/features/containers/syslog_container.py
index f2da956f5..c831af6f1 100644
--- a/extensions/standard-processors/tests/features/containers/syslog_container.py
+++ b/extensions/standard-processors/tests/features/containers/syslog_container.py
@@ -15,10 +15,10 @@
 #  limitations under the License.
 #
 
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 
 
-class SyslogContainer(Container):
+class SyslogContainer(LinuxContainer):
     def __init__(self, protocol, context):
         super(SyslogContainer, self).__init__("ubuntu:24.04", f"syslog-{protocol}-{context.scenario_id}", context.network)
         self.command = f'/bin/bash -c "echo Syslog {protocol} client started; while true; do logger --{protocol} -n minifi-primary-{context.scenario_id} -P 514 sample_log; sleep 1; done"'
diff --git a/extensions/standard-processors/tests/features/containers/tcp_client_container.py b/extensions/standard-processors/tests/features/containers/tcp_client_container.py
index 5da752523..74fcf1351 100644
--- a/extensions/standard-processors/tests/features/containers/tcp_client_container.py
+++ b/extensions/standard-processors/tests/features/containers/tcp_client_container.py
@@ -13,12 +13,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from minifi_test_framework.containers.container import Container
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.core.helpers import wait_for_condition
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 
 
-class TcpClientContainer(Container):
+class TcpClientContainer(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext):
         cmd = (
             "/bin/sh -c 'apk add netcat-openbsd && "
diff --git a/extensions/standard-processors/tests/features/core_functionality.feature b/extensions/standard-processors/tests/features/core_functionality.feature
index bab09863f..bae88e4cc 100644
--- a/extensions/standard-processors/tests/features/core_functionality.feature
+++ b/extensions/standard-processors/tests/features/core_functionality.feature
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-@CORE
+@CORE @SUPPORTS_WINDOWS
 Feature: Core flow functionalities
   Test core flow configuration functionalities
 
@@ -33,8 +33,8 @@ Feature: Core flow functionalities
 
     When all instances start up
 
-    Then at least one file with the content "first_custom_text" is placed in the "/tmp/output" directory in less than 20 seconds
-    And at least one file with the content "second_custom_text" is placed in the "/tmp/output" directory in less than 20 seconds
+    Then at least one file with the content "first_custom_text" is placed in the "/tmp/output" directory in less than 200 seconds
+    And at least one file with the content "second_custom_text" is placed in the "/tmp/output" directory in less than 200 seconds
 
   Scenario: A funnel can be used as a terminator
     Given a GenerateFlowFile processor with the "Data Format" property set to "Text"
diff --git a/extensions/standard-processors/tests/features/environment.py b/extensions/standard-processors/tests/features/environment.py
index 1edb297a3..a2c3531ce 100644
--- a/extensions/standard-processors/tests/features/environment.py
+++ b/extensions/standard-processors/tests/features/environment.py
@@ -12,8 +12,9 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-
+import os
 import platform
+
 import docker
 from minifi_test_framework.core.hooks import common_before_scenario
 from minifi_test_framework.core.hooks import common_after_scenario
@@ -30,6 +31,8 @@ def before_feature(context, feature):
 
 
 def is_minifi_image_alpine_based(context):
+    if os.name == 'nt':
+        return False
     client: docker.DockerClient = docker.from_env()
     container = client.containers.create(
         image=context.minifi_container_image,
diff --git a/extensions/standard-processors/tests/features/replace_text.feature b/extensions/standard-processors/tests/features/replace_text.feature
index f8a3fdb05..b24a0e873 100644
--- a/extensions/standard-processors/tests/features/replace_text.feature
+++ b/extensions/standard-processors/tests/features/replace_text.feature
@@ -13,7 +13,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-@CORE
+@CORE @SUPPORTS_WINDOWS
 Feature: Changing flowfile contents using the ReplaceText processor
 
   Scenario Outline: Replace text using Entire text mode
diff --git a/extensions/standard-processors/tests/features/steps/minifi_c2_server_container.py b/extensions/standard-processors/tests/features/steps/minifi_c2_server_container.py
index 75c4f4f3b..ff1d5d352 100644
--- a/extensions/standard-processors/tests/features/steps/minifi_c2_server_container.py
+++ b/extensions/standard-processors/tests/features/steps/minifi_c2_server_container.py
@@ -17,32 +17,34 @@
 
 import jks
 import os
-from OpenSSL import crypto
-from cryptography.hazmat.primitives.serialization import pkcs12, BestAvailableEncryption, load_pem_private_key
+
+from cryptography.hazmat.primitives import serialization
 from cryptography import x509
 from pathlib import Path
 
-from minifi_test_framework.containers.container import Container
+from cryptography.hazmat.primitives.serialization import load_pem_private_key, pkcs12, BestAvailableEncryption
+from minifi_test_framework.containers.container_linux import LinuxContainer
 from minifi_test_framework.core.helpers import wait_for_condition
 from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 from minifi_test_framework.core.ssl_utils import make_server_cert
 from minifi_test_framework.containers.file import File
 from minifi_test_framework.containers.host_file import HostFile
+from minifi_test_framework.core.ssl_utils import dump_key, dump_cert
 
 
-class MinifiC2Server(Container):
+class MinifiC2Server(LinuxContainer):
     def __init__(self, test_context: MinifiTestContext, ssl: bool = False):
         super().__init__("apache/nifi-minifi-c2:1.27.0", f"minifi-c2-server-{test_context.scenario_id}", test_context.network)
         if ssl:
             c2_cert, c2_key = make_server_cert(f"minifi-c2-server-{test_context.scenario_id}", test_context.root_ca_cert, test_context.root_ca_key)
-            pke = jks.PrivateKeyEntry.new('c2-server-cert', [crypto.dump_certificate(crypto.FILETYPE_ASN1, c2_cert)], crypto.dump_privatekey(crypto.FILETYPE_ASN1, c2_key), 'rsa_raw')
+            pke = jks.PrivateKeyEntry.new('c2-server-cert', [dump_cert(c2_cert, encoding_type=serialization.Encoding.DER)], dump_key(c2_key, encoding_type=serialization.Encoding.DER), 'rsa_raw')
             server_keystore = jks.KeyStore.new('jks', [pke])
             server_keystore_content = server_keystore.saves('abcdefgh')
             self.files.append(File("/opt/minifi-c2/minifi-c2-current/certs/minifi-c2-server-keystore.jks", server_keystore_content, permissions=0o644))
 
-            private_key_pem = crypto.dump_privatekey(crypto.FILETYPE_PEM, test_context.root_ca_key)
+            private_key_pem = dump_key(test_context.root_ca_key)
             private_key = load_pem_private_key(private_key_pem, password=None)
-            certificate_pem = crypto.dump_certificate(crypto.FILETYPE_PEM, test_context.root_ca_cert)
+            certificate_pem = dump_cert(test_context.root_ca_cert)
             certificate = x509.load_pem_x509_certificate(certificate_pem)
             pkcs12_data = pkcs12.serialize_key_and_certificates(
                 name=None,
diff --git a/extensions/standard-processors/tests/features/steps/minifi_controller_steps.py b/extensions/standard-processors/tests/features/steps/minifi_controller_steps.py
index e3a8641c8..02aea2c22 100644
--- a/extensions/standard-processors/tests/features/steps/minifi_controller_steps.py
+++ b/extensions/standard-processors/tests/features/steps/minifi_controller_steps.py
@@ -22,52 +22,52 @@ from minifi_test_framework.core.minifi_test_context import MinifiTestContext
 
 @given('controller socket properties are set up')
 def setup_controller_socket_properties(context: MinifiTestContext):
-    context.get_or_create_default_minifi_container().set_controller_socket_properties()
+    context.get_or_create_default_minifi_container().controller.set_controller_socket_properties()
 
 
 @when('MiNiFi config is updated through MiNiFi controller')
 def update_minifi_config_via_controller(context: MinifiTestContext):
-    context.get_or_create_default_minifi_container().update_flow_config_through_controller()
+    context.get_or_create_default_minifi_container().controller.update_flow_config_through_controller()
 
 
 @then('the updated config is persisted')
 def verify_updated_config_is_persisted(context: MinifiTestContext):
-    assert context.get_or_create_default_minifi_container().updated_config_is_persisted()
+    assert context.get_or_create_default_minifi_container().controller.updated_config_is_persisted()
 
 
 @when('the {component} component is stopped through MiNiFi controller')
 def stop_component_via_controller(context: MinifiTestContext, component: str):
-    context.get_or_create_default_minifi_container().stop_component_through_controller(component)
+    context.get_or_create_default_minifi_container().controller.stop_component_through_controller(component)
 
 
 @when('the {component} component is started through MiNiFi controller')
 def start_component_via_controller(context: MinifiTestContext, component: str):
-    context.get_or_create_default_minifi_container().start_component_through_controller(component)
+    context.get_or_create_default_minifi_container().controller.start_component_through_controller(component)
 
 
 @then('the {component} component is not running')
 def verify_component_not_running(context: MinifiTestContext, component: str):
-    assert not context.get_or_create_default_minifi_container().is_component_running(component)
+    assert not context.get_or_create_default_minifi_container().controller.is_component_running(component)
 
 
 @then('the {component} component is running')
 def verify_component_is_running(context: MinifiTestContext, component: str):
-    assert context.get_or_create_default_minifi_container().is_component_running(component)
+    assert context.get_or_create_default_minifi_container().controller.is_component_running(component)
 
 
 @then('connection \"{connection}\" can be seen through MiNiFi controller')
 def verify_connection_seen_via_controller(context: MinifiTestContext, connection: str):
-    assert context.get_or_create_default_minifi_container().connection_found_through_controller(connection)
+    assert context.get_or_create_default_minifi_container().controller.connection_found_through_controller(connection)
 
 
 @then('{connection_count:d} connections can be seen full through MiNiFi controller')
 def verify_full_connection_count(context: MinifiTestContext, connection_count: int):
-    assert context.get_or_create_default_minifi_container().get_full_connection_count() == connection_count
+    assert context.get_or_create_default_minifi_container().controller.get_full_connection_count() == connection_count
 
 
-@retry_check(10, 1)
+@retry_check(max_tries=5, retry_interval_seconds=1)
 def check_connection_size_through_controller(context: MinifiTestContext, connection: str, size: int, max_size: int) -> bool:
-    return context.get_or_create_default_minifi_container().get_connection_size(connection) == (size, max_size)
+    return context.get_or_create_default_minifi_container().controller.get_connection_size(connection) == (size, max_size)
 
 
 @then('connection \"{connection}\" has {size:d} size and {max_size:d} max size through MiNiFi controller')
@@ -75,9 +75,9 @@ def verify_connection_size_via_controller(context: MinifiTestContext, connection
     assert check_connection_size_through_controller(context, connection, size, max_size)
 
 
-@retry_check(10, 1)
+@retry_check(max_tries=10, retry_interval_seconds=1)
 def manifest_can_be_retrieved_through_minifi_controller(context: MinifiTestContext) -> bool:
-    manifest = context.get_or_create_default_minifi_container().get_manifest()
+    manifest = context.get_or_create_default_minifi_container().controller.get_manifest()
     return '"agentManifest": {' in manifest and '"componentManifest": {' in manifest and '"agentType": "cpp"' in manifest
 
 
@@ -88,4 +88,4 @@ def verify_manifest_retrieval_via_controller(context: MinifiTestContext):
 
 @then('debug bundle can be retrieved through MiNiFi controller')
 def verify_debug_bundle_retrieval_via_controller(context: MinifiTestContext):
-    assert context.get_or_create_default_minifi_container().create_debug_bundle()
+    assert context.get_or_create_default_minifi_container().controller.create_debug_bundle()
diff --git a/extensions/standard-processors/tests/features/steps/steps.py b/extensions/standard-processors/tests/features/steps/steps.py
index c35322c86..79167ed56 100644
--- a/extensions/standard-processors/tests/features/steps/steps.py
+++ b/extensions/standard-processors/tests/features/steps/steps.py
@@ -27,6 +27,8 @@ from minifi_test_framework.core.helpers import wait_for_condition
 from containers.syslog_container import SyslogContainer
 from containers.diag_slave_container import DiagSlave
 from containers.tcp_client_container import TcpClientContainer
+
+from minifi_test_framework.containers.minifi_protocol import set_up_ssl_properties
 from minifi_c2_server_container import MinifiC2Server
 
 
@@ -78,7 +80,7 @@ def setup_ssl_properties_for_c2(context: MinifiTestContext):
     context.get_or_create_default_minifi_container().set_property("nifi.c2.full.heartbeat", "false")
     context.get_or_create_default_minifi_container().set_property("nifi.c2.agent.class", "minifi-test-class")
     context.get_or_create_default_minifi_container().set_property("nifi.c2.agent.identifier", "minifi-test-id")
-    context.get_or_create_default_minifi_container().set_up_ssl_properties()
+    set_up_ssl_properties(context.get_or_create_default_minifi_container())
 
 
 @given("a MiNiFi C2 server is set up")


(nifi-minifi-cpp) 01/03: MINIFICPP-2708 Controller Service C API

Posted by ma...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

martinzink pushed a commit to branch apache-rusty
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git

commit 2494252f09793c3125c3e0ff396c7e1c56070114
Author: Martin Zink <ma...@apache.org>
AuthorDate: Mon Jan 26 18:12:19 2026 +0100

    MINIFICPP-2708 Controller Service C API
---
 Extensions.md                                      |   4 +-
 core-framework/include/agent/agent_docs.h          |  13 +-
 .../core/controller/ControllerServiceBase.h        |   6 +-
 .../core/controller/ControllerServiceFactoryImpl.h |   4 +-
 .../aws/tests/MultipartUploadStateStorageTest.cpp  |   2 +-
 .../controllers/NetworkPrioritizerService.h        |   2 +-
 .../include/core/controller/ControllerService.h    |   9 +-
 libminifi/include/utils/CControllerService.h       | 112 +++++++++++++++
 libminifi/include/utils/CProcessor.h               |   9 +-
 libminifi/src/controllers/SSLContextService.cpp    |   2 +-
 libminifi/src/minifi-c.cpp                         | 150 +++++++++++++++++++--
 libminifi/test/libtest/integration/HTTPHandlers.h  |  13 +-
 .../test/libtest/unit/ControllerServiceUtils.h     |   2 +-
 .../test/unit/NetworkPrioritizerServiceTests.cpp   |   2 +-
 .../minifi-cpp/core}/ControllerServiceMetadata.h   |   6 +-
 minifi-api/include/minifi-c/minifi-c.h             |  35 +++++
 minifi-api/include/minifi-cpp/agent/agent_docs.h   |   4 +-
 .../minifi-cpp/controllers/RecordSetReader.h       |   4 +-
 .../minifi-cpp/controllers/RecordSetWriter.h       |   4 +-
 .../controllers/SSLContextServiceInterface.h       |   7 +-
 ...trollerServiceApi.h => ControllerServiceType.h} |   6 +-
 ...inition.h => ControllerServiceTypeDefinition.h} |   2 +-
 .../core/controller/ControllerServiceDescriptor.h  |   1 +
 .../core/controller/ControllerServiceFactory.h     |   5 +-
 minifi-api/minifi-c-api.def                        |   3 +
 25 files changed, 347 insertions(+), 60 deletions(-)

diff --git a/Extensions.md b/Extensions.md
index c002c96ce..d0d402bf1 100644
--- a/Extensions.md
+++ b/Extensions.md
@@ -77,7 +77,9 @@ extern "C" void MinifiInitCppExtension(MinifiExtension* extension, MinifiConfig*
     .deinit = nullptr,
     .user_data = nullptr,
     .processors_count = 0,
-    .processors_ptr = nullptr
+    .processors_ptr = nullptr,
+    .controller_services_count = 0,
+    .controller_services_ptr = nullptr,
   };
   minifi::utils::MinifiCreateCppExtension(extension, &ext_create_info);
 }
diff --git a/core-framework/include/agent/agent_docs.h b/core-framework/include/agent/agent_docs.h
index 14544f2d6..4c7e25035 100644
--- a/core-framework/include/agent/agent_docs.h
+++ b/core-framework/include/agent/agent_docs.h
@@ -16,18 +16,17 @@
  */
 #pragma once
 
-#include "minifi-cpp/agent/agent_docs.h"
-
 #include <map>
 #include <string>
 #include <utility>
 #include <vector>
 
-#include "utils/StringUtils.h"
 #include "core/ClassName.h"
-#include "minifi-cpp/core/OutputAttribute.h"
-#include "minifi-cpp/core/ControllerServiceApi.h"
+#include "minifi-cpp/agent/agent_docs.h"
+#include "minifi-cpp/core/ControllerServiceType.h"
 #include "minifi-cpp/core/DynamicProperty.h"
+#include "minifi-cpp/core/OutputAttribute.h"
+#include "utils/StringUtils.h"
 
 namespace org::apache::nifi::minifi {
 
@@ -44,8 +43,8 @@ inline auto toVector(std::span<const core::OutputAttributeReference> attributes)
   return std::vector<core::OutputAttribute>(attributes.begin(), attributes.end());
 }
 
-inline auto toVector(std::span<const core::ControllerServiceApiDefinition> apis) {
-  return std::vector<core::ControllerServiceApi>(apis.begin(), apis.end());
+inline auto toVector(std::span<const core::ControllerServiceTypeDefinition> apis) {
+  return std::vector<core::ControllerServiceType>(apis.begin(), apis.end());
 }
 
 inline auto toVector(std::span<const core::DynamicPropertyDefinition> properties) {
diff --git a/core-framework/include/core/controller/ControllerServiceBase.h b/core-framework/include/core/controller/ControllerServiceBase.h
index 99c7f2a71..f0880cb4f 100644
--- a/core-framework/include/core/controller/ControllerServiceBase.h
+++ b/core-framework/include/core/controller/ControllerServiceBase.h
@@ -28,8 +28,8 @@
 #include "core/Connectable.h"
 #include "minifi-cpp/core/controller/ControllerServiceApi.h"
 #include "minifi-cpp/core/controller/ControllerServiceHandle.h"
-#include "minifi-cpp/core/ControllerServiceApiDefinition.h"
-#include "minifi-cpp/core/controller/ControllerServiceMetadata.h"
+#include "minifi-cpp/core/ControllerServiceTypeDefinition.h"
+#include "minifi-cpp/core/ControllerServiceMetadata.h"
 
 #define ADD_COMMON_VIRTUAL_FUNCTIONS_FOR_CONTROLLER_SERVICES \
   bool supportsDynamicProperties() const override { return SupportsDynamicProperties; }
@@ -96,7 +96,7 @@ class ControllerServiceBase : public ControllerServiceApi {
   }
 
 
-  static constexpr auto ImplementsApis = std::array<ControllerServiceApiDefinition, 0>{};
+  static constexpr auto ImplementsApis = std::array<ControllerServiceTypeDefinition, 0>{};
 
  protected:
   std::string name_;
diff --git a/core-framework/include/core/controller/ControllerServiceFactoryImpl.h b/core-framework/include/core/controller/ControllerServiceFactoryImpl.h
index 4060e9d18..a30bd48c6 100644
--- a/core-framework/include/core/controller/ControllerServiceFactoryImpl.h
+++ b/core-framework/include/core/controller/ControllerServiceFactoryImpl.h
@@ -17,10 +17,12 @@
 
 #pragma once
 
-#include <string>
 #include <memory>
+#include <string>
 #include <utility>
+
 #include "core/ClassName.h"
+#include "minifi-cpp/core/ControllerServiceMetadata.h"
 #include "minifi-cpp/core/controller/ControllerServiceFactory.h"
 
 namespace org::apache::nifi::minifi::core::controller {
diff --git a/extensions/aws/tests/MultipartUploadStateStorageTest.cpp b/extensions/aws/tests/MultipartUploadStateStorageTest.cpp
index c791e85c9..fc6f757be 100644
--- a/extensions/aws/tests/MultipartUploadStateStorageTest.cpp
+++ b/extensions/aws/tests/MultipartUploadStateStorageTest.cpp
@@ -32,7 +32,7 @@ class MultipartUploadStateStorageTestFixture {
   MultipartUploadStateStorageTestFixture() {
     LogTestController::getInstance().setDebug<minifi::aws::s3::MultipartUploadStateStorage>();
     const auto storage_uuid = minifi::utils::IdGenerator::getIdGenerator()->generate();
-    state_storage_ = std::make_unique<minifi::controllers::VolatileMapStateStorage>(core::controller::ControllerServiceMetadata{
+    state_storage_ = std::make_unique<minifi::controllers::VolatileMapStateStorage>(core::ControllerServiceMetadata{
       .uuid = storage_uuid,
       .name = "KeyValueStateStorage",
       .logger = logging::LoggerFactory<minifi::controllers::VolatileMapStateStorage>::getLogger(storage_uuid)
diff --git a/libminifi/include/controllers/NetworkPrioritizerService.h b/libminifi/include/controllers/NetworkPrioritizerService.h
index f893a9f68..684a72ce9 100644
--- a/libminifi/include/controllers/NetworkPrioritizerService.h
+++ b/libminifi/include/controllers/NetworkPrioritizerService.h
@@ -51,7 +51,7 @@ class NetworkPrioritizerService : public core::controller::ControllerServiceBase
 
  public:
   using ControllerServiceBase::ControllerServiceBase;
-  explicit NetworkPrioritizerService(core::controller::ControllerServiceMetadata metadata,
+  explicit NetworkPrioritizerService(core::ControllerServiceMetadata metadata,
                                      std::shared_ptr<utils::timeutils::Clock> clock)
       : ControllerServiceBase(std::move(metadata)),
         clock_(std::move(clock)) {
diff --git a/libminifi/include/core/controller/ControllerService.h b/libminifi/include/core/controller/ControllerService.h
index a4c26f547..561e41aee 100644
--- a/libminifi/include/core/controller/ControllerService.h
+++ b/libminifi/include/core/controller/ControllerService.h
@@ -27,7 +27,7 @@
 #include "core/ConfigurableComponentImpl.h"
 #include "core/Connectable.h"
 #include "minifi-cpp/core/controller/ControllerServiceApi.h"
-#include "minifi-cpp/core/ControllerServiceApiDefinition.h"
+#include "minifi-cpp/core/ControllerServiceTypeDefinition.h"
 
 namespace org::apache::nifi::minifi::core::controller {
 
@@ -44,6 +44,7 @@ class ControllerService : public ConfigurableComponentImpl, public CoreComponent
    public:
     explicit ControllerServiceDescriptorImpl(ControllerService& impl): impl_(impl) {}
     void setSupportedProperties(std::span<const PropertyReference> properties) override;
+    void setSupportedProperties(std::span<const Property> properties) override;
 
    private:
     ControllerService& impl_;
@@ -130,7 +131,7 @@ class ControllerService : public ConfigurableComponentImpl, public CoreComponent
     return impl_->supportsDynamicProperties();
   }
 
-  static constexpr auto ImplementsApis = std::array<ControllerServiceApiDefinition, 0>{};
+  static constexpr auto ImplementsApis = std::array<ControllerServiceTypeDefinition, 0>{};
 
  protected:
   std::unique_ptr<ControllerServiceApi> impl_;
@@ -146,6 +147,10 @@ inline void ControllerService::ControllerServiceDescriptorImpl::setSupportedProp
   impl_.setSupportedProperties(properties);
 }
 
+inline void ControllerService::ControllerServiceDescriptorImpl::setSupportedProperties(std::span<const Property> properties) {
+  impl_.setSupportedProperties(properties);
+}
+
 inline nonstd::expected<std::string, std::error_code> ControllerService::ControllerServiceContextImpl::getProperty(std::string_view name) const {
   return impl_.getProperty(name);
 }
diff --git a/libminifi/include/utils/CControllerService.h b/libminifi/include/utils/CControllerService.h
new file mode 100644
index 000000000..ca809b5cb
--- /dev/null
+++ b/libminifi/include/utils/CControllerService.h
@@ -0,0 +1,112 @@
+/**
+* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+#include "minifi-c/minifi-c.h"
+#include "minifi-cpp/Exception.h"
+#include "minifi-cpp/core/ControllerServiceMetadata.h"
+#include "minifi-cpp/core/Property.h"
+#include "minifi-cpp/core/controller/ControllerServiceApi.h"
+
+namespace org::apache::nifi::minifi::utils {
+class CControllerService;
+
+
+struct CControllerServiceClassDescription {
+  std::string name;
+
+  std::vector<core::Property> class_properties;
+
+  MinifiControllerServiceCallbacks callbacks;
+};
+
+class CControllerService final : public core::controller::ControllerServiceApi, public core::controller::ControllerServiceHandle {
+ public:
+  CControllerService(CControllerServiceClassDescription class_description, core::ControllerServiceMetadata metadata)
+    : class_description_(std::move(class_description)),
+      metadata_(std::move(metadata)) {
+    MinifiControllerServiceMetadata c_metadata;
+    auto uuid_str = metadata_.uuid.to_string();
+    c_metadata.uuid = MinifiStringView{.data = uuid_str.data(), .length = uuid_str.length()};
+    c_metadata.name = MinifiStringView{.data = metadata_.name.data(), .length = metadata_.name.length()};
+    c_metadata.logger = reinterpret_cast<MinifiLogger*>(&metadata_.logger);
+    impl_ = class_description_.callbacks.create(c_metadata);
+  }
+  CControllerService(CControllerServiceClassDescription class_description, core::ControllerServiceMetadata metadata, gsl::owner<void*> impl)
+      : class_description_(std::move(class_description)),
+        impl_(impl),
+        metadata_(std::move(metadata)) {}
+  ~CControllerService() override {
+    class_description_.callbacks.destroy(impl_);
+  }
+
+  [[nodiscard]] bool supportsDynamicProperties() const override {
+    return false;
+  }
+
+  void initialize(core::controller::ControllerServiceDescriptor& descriptor) override {
+    descriptor.setSupportedProperties(std::span(class_description_.class_properties));
+  }
+
+  void onEnable(core::controller::ControllerServiceContext& controller_service_context,
+      const std::shared_ptr<Configure>&,
+      const std::vector<std::shared_ptr<core::controller::ControllerServiceHandle>>&) override {
+    const auto enable_status = class_description_.callbacks.enable(impl_, reinterpret_cast<MinifiControllerServiceContext*>(&controller_service_context));
+    if (enable_status != MINIFI_STATUS_SUCCESS) {
+      throw Exception(PROCESS_SCHEDULE_EXCEPTION, "Could not enable controller service");
+    }
+  }
+
+  void notifyStop() override {
+    class_description_.callbacks.notifyStop(impl_);
+  }
+
+  ControllerServiceHandle* getControllerServiceHandle() override {
+    return this;
+  }
+
+  [[nodiscard]] std::string getName() const {
+    return metadata_.name;
+  }
+
+  [[nodiscard]] Identifier getUUID() const {
+    return metadata_.uuid;
+  }
+
+  [[nodiscard]] void* getImpl() const {
+    return this->impl_;
+  }
+
+  [[nodiscard]] const CControllerServiceClassDescription& getClassDescription() const {
+    return class_description_;
+  }
+
+ private:
+  CControllerServiceClassDescription class_description_;
+  gsl::owner<void*> impl_;
+  minifi::core::ControllerServiceMetadata metadata_;
+};
+
+void useCControllerServiceClassDescription(const MinifiControllerServiceClassDefinition& class_description,
+    const BundleIdentifier& bundle_id,
+    const std::function<void(ClassDescription, CControllerServiceClassDescription)>& fn);
+
+}  // namespace org::apache::nifi::minifi::utils
diff --git a/libminifi/include/utils/CProcessor.h b/libminifi/include/utils/CProcessor.h
index 057d2e59d..e6424ead5 100644
--- a/libminifi/include/utils/CProcessor.h
+++ b/libminifi/include/utils/CProcessor.h
@@ -18,6 +18,7 @@
 #pragma once
 
 #include <string>
+#include <utility>
 
 #include "minifi-cpp/core/Annotation.h"
 #include "core/ProcessorMetrics.h"
@@ -63,18 +64,18 @@ class CProcessor : public minifi::core::ProcessorApi {
   CProcessor(CProcessorClassDescription class_description, minifi::core::ProcessorMetadata metadata)
       : class_description_(std::move(class_description)),
         metrics_extension_(std::make_shared<CProcessorMetricsWrapper>(*this)) {
-    metadata_ = metadata;
+    metadata_ = std::move(metadata);
     MinifiProcessorMetadata c_metadata;
-    auto uuid_str = metadata.uuid.to_string();
+    const auto uuid_str = metadata.uuid.to_string();
     c_metadata.uuid = MinifiStringView{.data = uuid_str.data(), .length = uuid_str.length()};
-    c_metadata.name = MinifiStringView{.data = metadata.name.data(), .length = metadata.name.length()};
+    c_metadata.name = MinifiStringView{.data = metadata_.name.data(), .length = metadata_.name.length()};
     c_metadata.logger = reinterpret_cast<MinifiLogger*>(&metadata_.logger);
     impl_ = class_description_.callbacks.create(c_metadata);
   }
   CProcessor(CProcessorClassDescription class_description, minifi::core::ProcessorMetadata metadata, gsl::owner<void*> impl)
       : class_description_(std::move(class_description)),
         impl_(impl),
-        metadata_(metadata),
+        metadata_(std::move(metadata)),
         metrics_extension_(std::make_shared<CProcessorMetricsWrapper>(*this)) {}
   ~CProcessor() override {
     class_description_.callbacks.destroy(impl_);
diff --git a/libminifi/src/controllers/SSLContextService.cpp b/libminifi/src/controllers/SSLContextService.cpp
index dccdadeed..c0b02d085 100644
--- a/libminifi/src/controllers/SSLContextService.cpp
+++ b/libminifi/src/controllers/SSLContextService.cpp
@@ -596,7 +596,7 @@ void SSLContextService::verifyCertificateExpiration() {
 std::shared_ptr<SSLContextService> SSLContextService::createAndEnable(std::string_view name, const std::shared_ptr<Configure>& configuration) {
   auto uuid = utils::IdGenerator::getIdGenerator()->generate();
   auto logger = core::logging::LoggerFactory<SSLContextService>::getLogger(uuid);
-  auto service = std::make_shared<core::controller::ControllerService>(name, uuid, std::make_unique<SSLContextService>(core::controller::ControllerServiceMetadata{
+  auto service = std::make_shared<core::controller::ControllerService>(name, uuid, std::make_unique<SSLContextService>(core::ControllerServiceMetadata{
     .uuid = uuid,
     .name = std::string{name},
     .logger = logger,
diff --git a/libminifi/src/minifi-c.cpp b/libminifi/src/minifi-c.cpp
index bf9c5e84c..69e9ff935 100644
--- a/libminifi/src/minifi-c.cpp
+++ b/libminifi/src/minifi-c.cpp
@@ -36,12 +36,17 @@
 #include "minifi-cpp/core/logging/Logger.h"
 #include "minifi-cpp/core/state/PublishedMetricProvider.h"
 #include "utils/CProcessor.h"
+#include "utils/CControllerService.h"
 #include "utils/PropertyErrors.h"
 
 namespace minifi = org::apache::nifi::minifi;
 
 namespace {
 
+MinifiStringView minifiStringView(const std::string_view s) {
+  return MinifiStringView{.data = s.data(), .length = s.size()};
+}
+
 std::string toString(MinifiStringView sv) {
   return {sv.data, sv.length};
 }
@@ -146,6 +151,37 @@ class CProcessorFactory : public minifi::core::ProcessorFactory {
   minifi::utils::CProcessorClassDescription class_description_;
 };
 
+class CControllerServiceFactory : public minifi::core::controller::ControllerServiceFactory {
+ public:
+  CControllerServiceFactory(std::string group_name, std::string class_name, minifi::utils::CControllerServiceClassDescription class_description)
+    : group_name_(std::move(group_name)),
+      class_name_(std::move(class_name)),
+      class_description_(std::move(class_description)) {}
+  std::unique_ptr<minifi::core::controller::ControllerServiceApi> create(minifi::core::ControllerServiceMetadata metadata) override {
+    return std::make_unique<minifi::utils::CControllerService>(class_description_, std::move(metadata));
+  }
+
+  [[nodiscard]] std::string getGroupName() const override {
+    return group_name_;
+  }
+
+  [[nodiscard]] std::string getClassName() const override {
+    return class_name_;
+  }
+
+  CControllerServiceFactory() = delete;
+  CControllerServiceFactory(const CControllerServiceFactory&) = delete;
+  CControllerServiceFactory& operator=(const CControllerServiceFactory&) = delete;
+  CControllerServiceFactory(CControllerServiceFactory&&) = delete;
+  CControllerServiceFactory& operator=(CControllerServiceFactory&&) = delete;
+  ~CControllerServiceFactory() override = default;
+
+ private:
+  std::string group_name_;
+  std::string class_name_;
+  minifi::utils::CControllerServiceClassDescription class_description_;
+};
+
 }  // namespace
 
 namespace org::apache::nifi::minifi::utils {
@@ -221,6 +257,32 @@ void useCProcessorClassDescription(const MinifiProcessorClassDefinition& class_d
   fn(description, c_class_description);
 }
 
+void useCControllerServiceClassDescription(const MinifiControllerServiceClassDefinition& class_description,
+    const std::function<void(ClassDescription, CControllerServiceClassDescription)>& fn) {
+  std::vector<minifi::core::Property> properties;
+  properties.reserve(class_description.class_properties_count);
+  for (size_t i = 0; i < class_description.class_properties_count; ++i) {
+    properties.push_back(createProperty(&class_description.class_properties_ptr[i]));
+  }
+
+  auto name_segments = minifi::utils::string::split(toStringView(class_description.full_name), "::");
+  gsl_Assert(!name_segments.empty());
+
+  minifi::ClassDescription description{
+    .type_ = minifi::ResourceType::ControllerService,
+    .short_name_ = name_segments.back(),
+    .full_name_ = minifi::utils::string::join(".", name_segments),
+    .description_ = toString(class_description.description),
+    .class_properties_ = properties,
+  };
+
+  minifi::utils::CControllerServiceClassDescription c_class_description{
+    .name = name_segments.back(),
+    .class_properties = properties,
+    .callbacks = class_description.callbacks
+  };
+  fn(description, c_class_description);
+}
 }  // namespace org::apache::nifi::minifi::utils
 
 extern "C" {
@@ -233,7 +295,7 @@ MinifiExtension* MinifiCreateExtension(MinifiExtensionContext* extension_context
     .name = extension_name,
     .version = toString(extension_create_info->version)
   };
-  auto* extension = reinterpret_cast<org::apache::nifi::minifi::core::extension::Extension::Context*>(extension_context)->create(org::apache::nifi::minifi::core::extension::Extension::Info{
+  auto* extension = reinterpret_cast<minifi::core::extension::Extension::Context*>(extension_context)->create(org::apache::nifi::minifi::core::extension::Extension::Info{
     .name = toString(extension_create_info->name),
     .version = toString(extension_create_info->version),
     .deinit = extension_create_info->deinit,
@@ -245,14 +307,14 @@ MinifiExtension* MinifiCreateExtension(MinifiExtensionContext* extension_context
   return MINIFI_NULL;
 }
 
-MinifiStatus MinifiRegisterProcessor(MinifiExtension* extension_handle, const MinifiProcessorClassDefinition* processor) {
-  gsl_Assert(extension_handle);
+MinifiStatus MinifiRegisterProcessor(MinifiExtension* extension, const MinifiProcessorClassDefinition* processor) {
+  gsl_Assert(extension);
   gsl_Assert(processor);
-  auto extension_info = reinterpret_cast<org::apache::nifi::minifi::core::extension::Extension*>(extension_handle)->getInfo();
+  auto extension_info = reinterpret_cast<org::apache::nifi::minifi::core::extension::Extension*>(extension)->getInfo();
   if (!extension_info) {
     return MINIFI_STATUS_UNKNOWN_ERROR;
   }
-  minifi::BundleIdentifier bundle{
+  const minifi::BundleIdentifier bundle{
     .name = extension_info->name,
     .version = extension_info->version
   };
@@ -266,6 +328,28 @@ MinifiStatus MinifiRegisterProcessor(MinifiExtension* extension_handle, const Mi
   return MINIFI_STATUS_SUCCESS;
 }
 
+MinifiStatus MinifiRegisterControllerService(MinifiExtension* extension, const MinifiControllerServiceClassDefinition* controller_service) {
+  gsl_Assert(extension);
+  gsl_Assert(controller_service);
+  auto extension_info = reinterpret_cast<minifi::core::extension::Extension*>(extension)->getInfo();
+  if (!extension_info) {
+    return MINIFI_STATUS_UNKNOWN_ERROR;
+  }
+  const minifi::BundleIdentifier bundle{
+    .name = extension_info->name,
+    .version = extension_info->version
+  };
+  auto& bundle_components = minifi::ClassDescriptionRegistry::getMutableClassDescriptions()[bundle];
+  minifi::utils::useCControllerServiceClassDescription(*controller_service, [&] (const auto& description, const auto& c_class_description) {
+    minifi::core::ClassLoader::getDefaultClassLoader().getClassLoader(extension_info->name).registerClass(
+      c_class_description.name,
+      std::make_unique<CControllerServiceFactory>(extension_info->name, toString(controller_service->full_name), c_class_description));
+    bundle_components.controller_services.emplace_back(description);
+  });
+  return MINIFI_STATUS_SUCCESS;
+}
+
+
 MinifiExtension* MINIFI_CREATE_EXTENSION_FN(MinifiExtensionContext* extension_context, const MinifiExtensionCreateInfo* extension_create_info) {
   return MinifiCreateExtension(extension_context, extension_create_info);
 }
@@ -276,7 +360,7 @@ MinifiStatus MinifiProcessContextGetProperty(MinifiProcessContext* context, Mini
   auto result = reinterpret_cast<minifi::core::ProcessContext*>(context)->getProperty(toStringView(property_name),
       flowfile != MINIFI_NULL ? reinterpret_cast<std::shared_ptr<minifi::core::FlowFile>*>(flowfile)->get() : nullptr);
   if (result) {
-    result_cb(user_ctx, MinifiStringView{.data = result.value().data(), .length = result.value().length()});
+    result_cb(user_ctx, minifiStringView(result.value()));
     return MINIFI_STATUS_SUCCESS;
   }
   switch (static_cast<minifi::core::PropertyErrorCode>(result.error().value())) {
@@ -297,10 +381,7 @@ void MinifiConfigGet(MinifiExtensionContext* extension_context, MinifiStringView
   gsl_Assert(extension_context);
   auto value = reinterpret_cast<org::apache::nifi::minifi::core::extension::Extension::Context*>(extension_context)->config->get(toString(key));
   if (value) {
-    cb(user_ctx, MinifiStringView{
-      .data = value->data(),
-      .length = value->length()
-    });
+    cb(user_ctx, minifiStringView(*value));
   }
 }
 
@@ -444,7 +525,7 @@ MinifiBool MinifiFlowFileGetAttribute(MinifiProcessSession* session, MinifiFlowF
   if (!value.has_value()) {
     return false;
   }
-  cb(user_ctx, MinifiStringView{.data = value->data(), .length = value->size()});
+  cb(user_ctx, minifiStringView(*value));
   return true;
 }
 
@@ -453,8 +534,53 @@ void MinifiFlowFileGetAttributes(MinifiProcessSession* session, MinifiFlowFile*
   gsl_Assert(session != MINIFI_NULL);
   gsl_Assert(flowfile != MINIFI_NULL);
   for (auto& [key, value] : (*reinterpret_cast<std::shared_ptr<minifi::core::FlowFile>*>(flowfile))->getAttributes()) {
-    cb(user_ctx, MinifiStringView{.data = key.data(), .length = key.size()}, MinifiStringView{.data = value.data(), .length = value.size()});
+    cb(user_ctx, minifiStringView(key), minifiStringView(value));
+  }
+}
+
+MinifiStatus MinifiControllerServiceContextGetProperty(MinifiControllerServiceContext* context, MinifiStringView property_name,
+    void (*result_cb)(void* user_ctx, MinifiStringView result), void* user_ctx) {
+  gsl_Assert(context != MINIFI_NULL);
+  auto result = reinterpret_cast<minifi::core::controller::ControllerServiceContext*>(context)->getProperty(toStringView(property_name));
+  if (result) {
+    result_cb(user_ctx, minifiStringView(result.value()));
+    return MINIFI_STATUS_SUCCESS;
+  }
+  switch (static_cast<minifi::core::PropertyErrorCode>(result.error().value())) {
+    case minifi::core::PropertyErrorCode::NotSupportedProperty: return MINIFI_STATUS_NOT_SUPPORTED_PROPERTY;
+    case minifi::core::PropertyErrorCode::DynamicPropertiesNotSupported: return MINIFI_STATUS_DYNAMIC_PROPERTIES_NOT_SUPPORTED;
+    case minifi::core::PropertyErrorCode::PropertyNotSet: return MINIFI_STATUS_PROPERTY_NOT_SET;
+    case minifi::core::PropertyErrorCode::ValidationFailed: return MINIFI_STATUS_VALIDATION_FAILED;
+    default: return MINIFI_STATUS_UNKNOWN_ERROR;
   }
 }
 
+
+MinifiStatus MinifiProcessContextGetControllerService(
+    MinifiProcessContext* process_context,
+    const MinifiStringView controller_service_name,
+    const MinifiStringView controller_service_type,
+    void** controller_service_out) {
+  if (!controller_service_out) {
+    return MINIFI_STATUS_UNKNOWN_ERROR;
+  }
+
+  gsl_Assert(process_context != MINIFI_NULL);
+  const auto context = reinterpret_cast<minifi::core::ProcessContext*>(process_context);
+  const auto name_str = std::string{toStringView(controller_service_name)};
+  const auto service_shared_ptr = context->getControllerService(name_str, context->getProcessorInfo().getUUID());
+  if (!service_shared_ptr) {
+    return MINIFI_STATUS_VALIDATION_FAILED;
+  }
+  if (const minifi::utils::CControllerService* c_controller_service = dynamic_cast<minifi::utils::CControllerService*>(&*service_shared_ptr)) {
+    const auto class_description = c_controller_service->getClassDescription();
+    if (class_description.name == toStringView(controller_service_type)) {
+      *controller_service_out = c_controller_service->getImpl();
+      return MINIFI_STATUS_SUCCESS;
+    }
+  }
+  return MINIFI_STATUS_VALIDATION_FAILED;
+}
+
+
 }  // extern "C"
diff --git a/libminifi/test/libtest/integration/HTTPHandlers.h b/libminifi/test/libtest/integration/HTTPHandlers.h
index 25a0dba5d..40304948a 100644
--- a/libminifi/test/libtest/integration/HTTPHandlers.h
+++ b/libminifi/test/libtest/integration/HTTPHandlers.h
@@ -21,21 +21,22 @@
 #include <map>
 #include <memory>
 #include <optional>
+#include <set>
 #include <string>
+#include <unordered_map>
 #include <utility>
 #include <vector>
-#include <set>
-#include <unordered_map>
 
-#include "civetweb.h"
 #include "CivetServer.h"
-#include "concurrentqueue.h"
-#include "rapidjson/document.h"
-#include "utils/HTTPUtils.h"
 #include "ServerAwareHandler.h"
 #include "c2/C2Payload.h"
+#include "civetweb.h"
+#include "concurrentqueue.h"
+#include "minifi-cpp/properties/Configure.h"
 #include "properties/Configure.h"
+#include "rapidjson/document.h"
 #include "unit/Catch.h"
+#include "utils/HTTPUtils.h"
 
 namespace org::apache::nifi::minifi::test {
 
diff --git a/libminifi/test/libtest/unit/ControllerServiceUtils.h b/libminifi/test/libtest/unit/ControllerServiceUtils.h
index 7f541c1db..941ec8b73 100644
--- a/libminifi/test/libtest/unit/ControllerServiceUtils.h
+++ b/libminifi/test/libtest/unit/ControllerServiceUtils.h
@@ -29,7 +29,7 @@ std::unique_ptr<core::controller::ControllerService> make_controller_service(std
   if (!uuid) {
     uuid = minifi::utils::IdGenerator::getIdGenerator()->generate();
   }
-  auto controller_service_impl = std::make_unique<T>(core::controller::ControllerServiceMetadata{
+  auto controller_service_impl = std::make_unique<T>(core::ControllerServiceMetadata{
       .uuid = uuid.value(),
       .name = std::string{name},
       .logger = minifi::core::logging::LoggerFactory<T>::getLogger(uuid.value())
diff --git a/libminifi/test/unit/NetworkPrioritizerServiceTests.cpp b/libminifi/test/unit/NetworkPrioritizerServiceTests.cpp
index a54ba109a..a05d21ad2 100644
--- a/libminifi/test/unit/NetworkPrioritizerServiceTests.cpp
+++ b/libminifi/test/unit/NetworkPrioritizerServiceTests.cpp
@@ -31,7 +31,7 @@ std::shared_ptr<core::controller::ControllerService> createNetworkPrioritizerSer
     const std::shared_ptr<utils::timeutils::Clock>& clock = std::make_shared<minifi::test::utils::ManualClock>()) {
   return std::make_shared<core::controller::ControllerService>(
     name, utils::Identifier{},
-    std::make_unique<minifi::controllers::NetworkPrioritizerService>(core::controller::ControllerServiceMetadata{
+    std::make_unique<minifi::controllers::NetworkPrioritizerService>(core::ControllerServiceMetadata{
       .uuid = utils::Identifier{},
       .name = name,
       .logger = logging::LoggerFactory<minifi::controllers::NetworkPrioritizerService>::getLogger()
diff --git a/minifi-api/include/minifi-cpp/core/controller/ControllerServiceMetadata.h b/minifi-api/common/include/minifi-cpp/core/ControllerServiceMetadata.h
similarity index 84%
rename from minifi-api/include/minifi-cpp/core/controller/ControllerServiceMetadata.h
rename to minifi-api/common/include/minifi-cpp/core/ControllerServiceMetadata.h
index 3c6fb63b7..22200a84d 100644
--- a/minifi-api/include/minifi-cpp/core/controller/ControllerServiceMetadata.h
+++ b/minifi-api/common/include/minifi-cpp/core/ControllerServiceMetadata.h
@@ -1,5 +1,5 @@
 /**
- * Licensed to the Apache Software Foundation (ASF) under one or more
+* Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
  * this work for additional information regarding copyright ownership.
  * The ASF licenses this file to You under the Apache License, Version 2.0
@@ -21,7 +21,7 @@
 #include "minifi-cpp/utils/Id.h"
 #include "minifi-cpp/core/logging/Logger.h"
 
-namespace org::apache::nifi::minifi::core::controller {
+namespace org::apache::nifi::minifi::core {
 
 struct ControllerServiceMetadata {
   utils::Identifier uuid;
@@ -29,4 +29,4 @@ struct ControllerServiceMetadata {
   std::shared_ptr<logging::Logger> logger;
 };
 
-}  // namespace org::apache::nifi::minifi::core::controller
+}  // namespace org::apache::nifi::minifi::core
diff --git a/minifi-api/include/minifi-c/minifi-c.h b/minifi-api/include/minifi-c/minifi-c.h
index 86dc7b407..b689ce7ad 100644
--- a/minifi-api/include/minifi-c/minifi-c.h
+++ b/minifi-api/include/minifi-c/minifi-c.h
@@ -100,6 +100,8 @@ typedef struct MinifiExtensionContext MinifiExtensionContext;
 typedef struct MinifiPublishedMetrics MinifiPublishedMetrics;
 typedef struct MinifiAgent MinifiAgent;
 
+typedef struct MinifiControllerServiceContext MinifiControllerServiceContext;
+
 typedef enum MinifiStatus : uint32_t {
   MINIFI_STATUS_SUCCESS = 0,
   MINIFI_STATUS_UNKNOWN_ERROR = 1,
@@ -153,6 +155,12 @@ typedef struct MinifiProcessorMetadata {
   MinifiLogger* logger;  // borrowed reference, live until the processor is live
 } MinifiProcessorMetadata;
 
+typedef struct MinifiControllerServiceMetadata {
+  MinifiStringView uuid;
+  MinifiStringView name;
+  MinifiLogger* logger;  // borrowed reference, live until the controller service is live
+} MinifiControllerServiceMetadata;
+
 typedef struct MinifiProcessorCallbacks {
   MINIFI_OWNED void*(*create)(MinifiProcessorMetadata);
   void(*destroy)(MINIFI_OWNED void*);
@@ -164,6 +172,13 @@ typedef struct MinifiProcessorCallbacks {
   MINIFI_OWNED MinifiPublishedMetrics*(*calculateMetrics)(void*);
 } MinifiProcessorCallbacks;
 
+typedef struct MinifiControllerServiceCallbacks {
+  MINIFI_OWNED void*(*create)(MinifiControllerServiceMetadata);
+  void(*destroy)(MINIFI_OWNED void*);
+  MinifiStatus(*enable)(void*, MinifiControllerServiceContext*);
+  void(*notifyStop)(void*);
+} MinifiControllerServiceCallbacks;
+
 typedef struct MinifiProcessorClassDefinition {
   MinifiStringView full_name;  // '::'-delimited fully qualified name e.g. 'org::apache::nifi::minifi::GenerateFlowFile'
   MinifiStringView description;
@@ -183,6 +198,16 @@ typedef struct MinifiProcessorClassDefinition {
   MinifiProcessorCallbacks callbacks;
 } MinifiProcessorClassDefinition;
 
+typedef struct MinifiControllerServiceClassDefinition {
+  MinifiStringView full_name;  // '::'-delimited fully qualified name e.g. 'org::apache::nifi::minifi::extensions::gcp::GCPCredentialsControllerService
+  MinifiStringView description;
+  size_t class_properties_count;
+  const MinifiPropertyDefinition* class_properties_ptr;
+
+  MinifiControllerServiceCallbacks callbacks;
+} MinifiControllerServiceClassDefinition;
+
+
 typedef struct MinifiExtensionCreateInfo {
   MinifiStringView name;
   MinifiStringView version;
@@ -194,12 +219,17 @@ MinifiExtension* MINIFI_CREATE_EXTENSION_FN(MinifiExtensionContext* extension_co
 
 MinifiStatus MinifiRegisterProcessor(MinifiExtension* extension, const MinifiProcessorClassDefinition* processor);
 
+MinifiStatus MinifiRegisterControllerService(MinifiExtension* extension, const MinifiControllerServiceClassDefinition* controller_service);
+
 MINIFI_OWNED MinifiPublishedMetrics* MinifiPublishedMetricsCreate(size_t count, const MinifiStringView* metric_names, const double* metric_values);
 
 MinifiStatus MinifiProcessContextGetProperty(MinifiProcessContext* context, MinifiStringView property_name, MinifiFlowFile* flowfile,
                                              void(*cb)(void* user_ctx, MinifiStringView property_value), void* user_ctx);
 MinifiBool MinifiProcessContextHasNonEmptyProperty(MinifiProcessContext* context, MinifiStringView property_name);
 
+MinifiStatus MinifiProcessContextGetControllerService(
+    MinifiProcessContext* process_context, MinifiStringView controller_service_name, MinifiStringView controller_service_type, void** controller_service_out);
+
 void MinifiLoggerSetMaxLogSize(MinifiLogger*, int32_t);
 void MinifiLoggerLogString(MinifiLogger*, MinifiLogLevel, MinifiStringView);
 MinifiBool MinifiLoggerShouldLog(MinifiLogger*, MinifiLogLevel);
@@ -226,6 +256,11 @@ MinifiBool MinifiFlowFileGetAttribute(MinifiProcessSession* session, MinifiFlowF
                                       void(*cb)(void* user_ctx, MinifiStringView attribute_value), void* user_ctx);
 void MinifiFlowFileGetAttributes(MinifiProcessSession* session, MinifiFlowFile* flowfile, void(*cb)(void* user_ctx, MinifiStringView attribute_name, MinifiStringView attribute_value), void* user_ctx);
 
+MinifiStatus MinifiControllerServiceContextGetProperty(MinifiControllerServiceContext* context,
+    MinifiStringView property_name,
+    void(*cb)(void* user_ctx, MinifiStringView property_value),
+    void* user_ctx);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif  // __cplusplus
diff --git a/minifi-api/include/minifi-cpp/agent/agent_docs.h b/minifi-api/include/minifi-cpp/agent/agent_docs.h
index 61a759bed..781dfb735 100644
--- a/minifi-api/include/minifi-cpp/agent/agent_docs.h
+++ b/minifi-api/include/minifi-cpp/agent/agent_docs.h
@@ -22,7 +22,7 @@
 #include <vector>
 
 #include "minifi-cpp/core/Annotation.h"
-#include "minifi-cpp/core/ControllerServiceApi.h"
+#include "minifi-cpp/core/ControllerServiceType.h"
 #include "minifi-cpp/core/DynamicProperty.h"
 #include "minifi-cpp/core/OutputAttribute.h"
 #include "minifi-cpp/core/Property.h"
@@ -44,7 +44,7 @@ struct ClassDescription {
   std::vector<core::DynamicProperty> dynamic_properties_{};
   std::vector<core::Relationship> class_relationships_{};
   std::vector<core::OutputAttribute> output_attributes_{};
-  std::vector<core::ControllerServiceApi> api_implementations{};
+  std::vector<core::ControllerServiceType> api_implementations{};
   bool supports_dynamic_properties_ = false;
   bool supports_dynamic_relationships_ = false;
   std::string inputRequirement_{};
diff --git a/minifi-api/include/minifi-cpp/controllers/RecordSetReader.h b/minifi-api/include/minifi-cpp/controllers/RecordSetReader.h
index db7c26737..dff30ce1c 100644
--- a/minifi-api/include/minifi-cpp/controllers/RecordSetReader.h
+++ b/minifi-api/include/minifi-cpp/controllers/RecordSetReader.h
@@ -17,7 +17,7 @@
 #pragma once
 
 #include "minifi-cpp/core/controller/ControllerServiceHandle.h"
-#include "minifi-cpp/core/ControllerServiceApiDefinition.h"
+#include "minifi-cpp/core/ControllerServiceTypeDefinition.h"
 #include "minifi-cpp/core/Record.h"
 #include "utils/Enum.h"
 #include "utils/ProcessorConfigUtils.h"
@@ -28,7 +28,7 @@ namespace org::apache::nifi::minifi::core {
 
 class RecordSetReader : public controller::ControllerServiceHandle {
  public:
-  static constexpr auto ProvidesApi = core::ControllerServiceApiDefinition{
+  static constexpr auto ProvidesApi = core::ControllerServiceTypeDefinition{
     .artifact = "minifi-system",
     .group = "org.apache.nifi.minifi",
     .type = "org.apache.nifi.minifi.core.RecordSetReader",
diff --git a/minifi-api/include/minifi-cpp/controllers/RecordSetWriter.h b/minifi-api/include/minifi-cpp/controllers/RecordSetWriter.h
index 10e17f536..9e131168a 100644
--- a/minifi-api/include/minifi-cpp/controllers/RecordSetWriter.h
+++ b/minifi-api/include/minifi-cpp/controllers/RecordSetWriter.h
@@ -18,7 +18,7 @@
 
 #include "minifi-cpp/core/controller/ControllerServiceHandle.h"
 
-#include "minifi-cpp/core/ControllerServiceApiDefinition.h"
+#include "minifi-cpp/core/ControllerServiceTypeDefinition.h"
 #include "minifi-cpp/core/FlowFile.h"
 #include "minifi-cpp/core/ProcessSession.h"
 #include "minifi-cpp/core/Record.h"
@@ -28,7 +28,7 @@ namespace org::apache::nifi::minifi::core {
 
 class RecordSetWriter : public controller::ControllerServiceHandle {
  public:
-  static constexpr auto ProvidesApi = core::ControllerServiceApiDefinition{
+  static constexpr auto ProvidesApi = core::ControllerServiceTypeDefinition{
     .artifact = "minifi-system",
     .group = "org.apache.nifi.minifi",
     .type = "org.apache.nifi.minifi.core.RecordSetWriter",
diff --git a/minifi-api/include/minifi-cpp/controllers/SSLContextServiceInterface.h b/minifi-api/include/minifi-cpp/controllers/SSLContextServiceInterface.h
index bc21a2092..0c17e260b 100644
--- a/minifi-api/include/minifi-cpp/controllers/SSLContextServiceInterface.h
+++ b/minifi-api/include/minifi-cpp/controllers/SSLContextServiceInterface.h
@@ -16,13 +16,12 @@
  */
 #pragma once
 
-#include <iostream>
+#include <filesystem>
 #include <memory>
 #include <string>
 #include <utility>
-#include <filesystem>
 
-#include "minifi-cpp/core/ControllerServiceApiDefinition.h"
+#include "minifi-cpp/core/ControllerServiceTypeDefinition.h"
 #include "minifi-cpp/agent/agent_version.h"
 #include "minifi-cpp/core/controller/ControllerServiceHandle.h"
 
@@ -38,7 +37,7 @@ namespace org::apache::nifi::minifi::controllers {
  */
 class SSLContextServiceInterface : public core::controller::ControllerServiceHandle {
  public:
-  static constexpr auto ProvidesApi = core::ControllerServiceApiDefinition{
+  static constexpr auto ProvidesApi = core::ControllerServiceTypeDefinition{
     .artifact = "minifi-system",
     .group = "org.apache.nifi.minifi",
     .type = "org.apache.nifi.minifi.controllers.SSLContextServiceInterface",
diff --git a/minifi-api/include/minifi-cpp/core/ControllerServiceApi.h b/minifi-api/include/minifi-cpp/core/ControllerServiceType.h
similarity index 84%
rename from minifi-api/include/minifi-cpp/core/ControllerServiceApi.h
rename to minifi-api/include/minifi-cpp/core/ControllerServiceType.h
index fe0d450f7..6d5b39de3 100644
--- a/minifi-api/include/minifi-cpp/core/ControllerServiceApi.h
+++ b/minifi-api/include/minifi-cpp/core/ControllerServiceType.h
@@ -17,16 +17,16 @@
 #pragma once
 
 #include <string>
-#include "minifi-cpp/core/ControllerServiceApiDefinition.h"
+#include "minifi-cpp/core/ControllerServiceTypeDefinition.h"
 
 namespace org::apache::nifi::minifi::core {
 
-struct ControllerServiceApi {
+struct ControllerServiceType {
   std::string artifact;
   std::string group;
   std::string type;
 
-  ControllerServiceApi(const ControllerServiceApiDefinition& definition)  // NOLINT(runtime/explicit)
+  ControllerServiceType(const ControllerServiceTypeDefinition& definition)  // NOLINT(runtime/explicit)
     : artifact(definition.artifact),
       group(definition.group),
       type(definition.type) {}
diff --git a/minifi-api/include/minifi-cpp/core/ControllerServiceApiDefinition.h b/minifi-api/include/minifi-cpp/core/ControllerServiceTypeDefinition.h
similarity index 96%
rename from minifi-api/include/minifi-cpp/core/ControllerServiceApiDefinition.h
rename to minifi-api/include/minifi-cpp/core/ControllerServiceTypeDefinition.h
index 37084baea..f40d871a6 100644
--- a/minifi-api/include/minifi-cpp/core/ControllerServiceApiDefinition.h
+++ b/minifi-api/include/minifi-cpp/core/ControllerServiceTypeDefinition.h
@@ -20,7 +20,7 @@
 
 namespace org::apache::nifi::minifi::core {
 
-struct ControllerServiceApiDefinition {
+struct ControllerServiceTypeDefinition {
   std::string_view artifact;
   std::string_view group;
   std::string_view type;
diff --git a/minifi-api/include/minifi-cpp/core/controller/ControllerServiceDescriptor.h b/minifi-api/include/minifi-cpp/core/controller/ControllerServiceDescriptor.h
index 525a6d6ce..39fb6da89 100644
--- a/minifi-api/include/minifi-cpp/core/controller/ControllerServiceDescriptor.h
+++ b/minifi-api/include/minifi-cpp/core/controller/ControllerServiceDescriptor.h
@@ -26,6 +26,7 @@ class ControllerServiceDescriptor {
   virtual ~ControllerServiceDescriptor() = default;
 
   virtual void setSupportedProperties(std::span<const PropertyReference> properties) = 0;
+  virtual void setSupportedProperties(std::span<const Property> properties) = 0;
 };
 
 }  // namespace org::apache::nifi::minifi::core::controller
diff --git a/minifi-api/include/minifi-cpp/core/controller/ControllerServiceFactory.h b/minifi-api/include/minifi-cpp/core/controller/ControllerServiceFactory.h
index 725944bfa..aafc571dc 100644
--- a/minifi-api/include/minifi-cpp/core/controller/ControllerServiceFactory.h
+++ b/minifi-api/include/minifi-cpp/core/controller/ControllerServiceFactory.h
@@ -17,10 +17,11 @@
 
 #pragma once
 
-#include <string>
 #include <memory>
+#include <string>
 #include <utility>
-#include "minifi-cpp/core/controller/ControllerServiceMetadata.h"
+
+#include "minifi-cpp/core/ControllerServiceMetadata.h"
 
 namespace org::apache::nifi::minifi::core::controller {
 
diff --git a/minifi-api/minifi-c-api.def b/minifi-api/minifi-c-api.def
index a22f8c696..9fabf206d 100644
--- a/minifi-api/minifi-c-api.def
+++ b/minifi-api/minifi-c-api.def
@@ -2,9 +2,12 @@ LIBRARY core-minifi.dll
 EXPORTS
   MinifiCreateExtension
   MinifiRegisterProcessor
+  MinifiRegisterControllerService
   MinifiPublishedMetricsCreate
+  MinifiProcessContextGetControllerService
   MinifiProcessContextGetProperty
   MinifiProcessContextHasNonEmptyProperty
+  MinifiControllerServiceContextGetProperty
   MinifiLoggerSetMaxLogSize
   MinifiLoggerLogString
   MinifiLoggerShouldLog