From 7204b5616433c3d16ee11d5a2b1bb183318f854c Mon Sep 17 00:00:00 2001 From: Gabor Gyimesi Date: Fri, 20 Sep 2024 11:01:01 +0200 Subject: [PATCH] MINIFICPP-2378 Add support for parameters in controller services --- .../tests/unit/FlowJsonTests.cpp | 120 ++++++++++++++++++ .../tests/unit/YamlConfigurationTests.cpp | 106 ++++++++++++++++ .../src/core/flow/StructuredConfiguration.cpp | 4 +- 3 files changed, 228 insertions(+), 2 deletions(-) diff --git a/extensions/standard-processors/tests/unit/FlowJsonTests.cpp b/extensions/standard-processors/tests/unit/FlowJsonTests.cpp index 5cf07b868d..7b7be3d583 100644 --- a/extensions/standard-processors/tests/unit/FlowJsonTests.cpp +++ b/extensions/standard-processors/tests/unit/FlowJsonTests.cpp @@ -1028,4 +1028,124 @@ TEST_CASE("NiFi flow json can use alternative targetUris field") { REQUIRE(port->getProperty("Port UUID") == "00000000-0000-0000-0000-000000000005"); } + +TEST_CASE("Test parameters in controller services") { + ConfigurationTestController test_controller; + auto context = test_controller.getContext(); + auto encrypted_parameter_value = minifi::utils::crypto::property_encryption::encrypt("secret1!!1!", *context.sensitive_values_encryptor); + auto encrypted_sensitive_property_value = minifi::utils::crypto::property_encryption::encrypt("#{my_value_1}", *context.sensitive_values_encryptor); + core::flow::AdaptiveConfiguration json_config(context); + + static const std::string CONFIG_JSON = + fmt::format(R"( +{{ + "parameterContexts": [ + {{ + "identifier": "721e10b7-8e00-3188-9a27-476cca376978", + "name": "my-context", + "description": "my parameter context", + "parameters": [ + {{ + "name": "my_value_1", + "description": "", + "sensitive": true, + "value": "{}" + }}, + {{ + "name": "my_value_2", + "description": "", + "sensitive": false, + "value": "/opt/secrets/private-key.pem" + }} + ] + }} + ], + "rootGroup": {{ + "name": "MiNiFi Flow", + "processors": [], + "controllerServices": [{{ + "identifier": "a00f8722-2419-44ee-929c-ad68644ad557", + "name": "SSLContextService", + "type": "org.apache.nifi.minifi.controllers.SSLContextService", + "properties": {{ + "Passphrase": "{}", + "Private Key": "#{{my_value_2}}", + "Use System Cert Store": "true" + }} + }}], + "parameterContextName": "my-context" + }} +}})", encrypted_parameter_value, encrypted_sensitive_property_value); + + std::unique_ptr flow = json_config.getRootFromPayload(CONFIG_JSON); + REQUIRE(flow); + auto* controller = flow->findControllerService("SSLContextService"); + REQUIRE(controller); + auto impl = controller->getControllerServiceImplementation(); + CHECK(impl->getProperty("Passphrase").value() == "secret1!!1!"); + CHECK(impl->getProperty("Private Key").value() == "/opt/secrets/private-key.pem"); +} + +TEST_CASE("Parameters can be used in controller services in nested process groups") { + ConfigurationTestController test_controller; + auto context = test_controller.getContext(); + auto encrypted_parameter_value = minifi::utils::crypto::property_encryption::encrypt("secret1!!1!", *context.sensitive_values_encryptor); + auto encrypted_sensitive_property_value = minifi::utils::crypto::property_encryption::encrypt("#{my_value_1}", *context.sensitive_values_encryptor); + core::flow::AdaptiveConfiguration json_config(context); + + static const std::string CONFIG_JSON = + fmt::format(R"( +{{ + "parameterContexts": [ + {{ + "identifier": "721e10b7-8e00-3188-9a27-476cca376978", + "name": "my-context", + "description": "my parameter context", + "parameters": [ + {{ + "name": "my_value_1", + "description": "", + "sensitive": true, + "value": "{}" + }}, + {{ + "name": "my_value_2", + "description": "", + "sensitive": false, + "value": "/opt/secrets/private-key.pem" + }} + ] + }} + ], + "rootGroup": {{ + "name": "MiNiFi Flow", + "processors": [], + "processGroups": [{{ + "name": "MiNiFi SubFlow", + "processors": [], + "controllerServices": [{{ + "identifier": "a00f8722-2419-44ee-929c-ad68644ad557", + "name": "SSLContextService", + "type": "org.apache.nifi.minifi.controllers.SSLContextService", + "properties": {{ + "Passphrase": "{}", + "Private Key": "#{{my_value_2}}", + "Use System Cert Store": "true" + }} + }}], + "parameterContextName": "my-context" + }}] + }} +}})", encrypted_parameter_value, encrypted_sensitive_property_value); + + std::unique_ptr flow = json_config.getRootFromPayload(CONFIG_JSON); + REQUIRE(flow); + auto* controller = flow->findControllerService("SSLContextService", core::ProcessGroup::Traverse::IncludeChildren); + REQUIRE(controller); + auto impl = controller->getControllerServiceImplementation(); + REQUIRE(impl); + CHECK(impl->getProperty("Passphrase").value() == "secret1!!1!"); + CHECK(impl->getProperty("Private Key").value() == "/opt/secrets/private-key.pem"); +} + } // namespace org::apache::nifi::minifi::test diff --git a/extensions/standard-processors/tests/unit/YamlConfigurationTests.cpp b/extensions/standard-processors/tests/unit/YamlConfigurationTests.cpp index bf444a0cf1..694e63b4f3 100644 --- a/extensions/standard-processors/tests/unit/YamlConfigurationTests.cpp +++ b/extensions/standard-processors/tests/unit/YamlConfigurationTests.cpp @@ -1769,4 +1769,110 @@ Parameter Context Name: my-context CHECK(values[1] == "value2"); } +TEST_CASE("Test parameters in controller services", "[YamlConfiguration]") { + ConfigurationTestController test_controller; + auto context = test_controller.getContext(); + auto encrypted_parameter_value_1 = minifi::utils::crypto::property_encryption::encrypt("secret1!!1!", *context.sensitive_values_encryptor); + auto encrypted_sensitive_property_value_1 = minifi::utils::crypto::property_encryption::encrypt("#{my_value_1}", *context.sensitive_values_encryptor); + core::YamlConfiguration yaml_config(context); + + static const std::string TEST_CONFIG_YAML = + fmt::format(R"( +MiNiFi Config Version: 3 +Flow Controller: + name: flowconfig +Parameter Contexts: + - id: 721e10b7-8e00-3188-9a27-476cca376978 + name: my-context + description: my parameter context + Parameters: + - name: my_value_1 + description: '' + sensitive: true + value: {} + - name: my_value_2 + description: '' + sensitive: false + value: /opt/secrets/private-key.pem +Processors: [] +Controller Services: +- id: a00f8722-2419-44ee-929c-ad68644ad557 + name: SSLContextService + type: org.apache.nifi.minifi.controllers.SSLContextService + Properties: + CA Certificate: + Client Certificate: + Passphrase: {} + Private Key: "#{{my_value_2}}" + Use System Cert Store: 'true' +Parameter Context Name: my-context + )", encrypted_parameter_value_1, encrypted_sensitive_property_value_1); + + std::unique_ptr flow = yaml_config.getRootFromPayload(TEST_CONFIG_YAML); + REQUIRE(flow); + auto* controller = flow->findControllerService("SSLContextService"); + REQUIRE(controller); + auto impl = controller->getControllerServiceImplementation(); + CHECK(impl->getProperty("Passphrase").value() == "secret1!!1!"); + CHECK(impl->getProperty("Private Key").value() == "/opt/secrets/private-key.pem"); +} + +TEST_CASE("Parameters can be used in controller services in nested process groups", "[YamlConfiguration]") { + ConfigurationTestController test_controller; + auto context = test_controller.getContext(); + auto encrypted_parameter_value_1 = minifi::utils::crypto::property_encryption::encrypt("secret1!!1!", *context.sensitive_values_encryptor); + auto encrypted_sensitive_property_value_1 = minifi::utils::crypto::property_encryption::encrypt("#{my_value_1}", *context.sensitive_values_encryptor); + core::YamlConfiguration yaml_config(context); + + static const std::string TEST_CONFIG_YAML = + fmt::format(R"( +MiNiFi Config Version: 3 +Flow Controller: + name: Simple TailFile +Parameter Contexts: + - id: 123e10b7-8e00-3188-9a27-476cca376456 + name: sub-context + description: my sub context + Parameters: + - name: my_value_1 + description: '' + sensitive: true + value: {} + - name: my_value_2 + description: '' + sensitive: false + value: /opt/secrets/private-key.pem +Processors: [] +Controller Services: [] +Input Ports: [] +Output Ports: [] +Funnels: [] +Connections: [] +Process Groups: + - id: 2a3aaf32-8574-4fa7-b720-84001f8dde43 + name: Sub process group + Processors: [] + Controller Services: + - id: a00f8722-2419-44ee-929c-ad68644ad557 + name: SSLContextService + type: org.apache.nifi.minifi.controllers.SSLContextService + Properties: + CA Certificate: + Client Certificate: + Passphrase: {} + Private Key: "#{{my_value_2}}" + Use System Cert Store: 'true' + Parameter Context Name: sub-context + )", encrypted_parameter_value_1, encrypted_sensitive_property_value_1); + + std::unique_ptr flow = yaml_config.getRootFromPayload(TEST_CONFIG_YAML); + REQUIRE(flow); + auto* controller = flow->findControllerService("SSLContextService", core::ProcessGroup::Traverse::IncludeChildren); + REQUIRE(controller); + auto impl = controller->getControllerServiceImplementation(); + REQUIRE(impl); + CHECK(impl->getProperty("Passphrase").value() == "secret1!!1!"); + CHECK(impl->getProperty("Private Key").value() == "/opt/secrets/private-key.pem"); +} + } // namespace org::apache::nifi::minifi::test diff --git a/libminifi/src/core/flow/StructuredConfiguration.cpp b/libminifi/src/core/flow/StructuredConfiguration.cpp index 9d0b6c75db..d79550b460 100644 --- a/libminifi/src/core/flow/StructuredConfiguration.cpp +++ b/libminifi/src/core/flow/StructuredConfiguration.cpp @@ -540,9 +540,9 @@ void StructuredConfiguration::parseControllerServices(const Node& controller_ser controller_service_node->initialize(); if (Node propertiesNode = service_node[schema_.controller_service_properties]) { // we should propagate properties to the node and to the implementation - parsePropertiesNode(propertiesNode, *controller_service_node, name, nullptr); + parsePropertiesNode(propertiesNode, *controller_service_node, name, parent_group->getParameterContext()); if (auto controllerServiceImpl = controller_service_node->getControllerServiceImplementation(); controllerServiceImpl) { - parsePropertiesNode(propertiesNode, *controllerServiceImpl, name, nullptr); + parsePropertiesNode(propertiesNode, *controllerServiceImpl, name, parent_group->getParameterContext()); } }