diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/pom.xml b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/pom.xml index 047c5810fb3c..3dce83e0ed7e 100644 --- a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/pom.xml +++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/pom.xml @@ -22,8 +22,6 @@ nifi-standard-rules jar - - diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/RestrictLoadBalancing.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/RestrictLoadBalancing.java new file mode 100644 index 000000000000..28f30765ac88 --- /dev/null +++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/java/org/apache/nifi/flowanalysis/rules/RestrictLoadBalancing.java @@ -0,0 +1,254 @@ +/* + * 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. + */ +package org.apache.nifi.flowanalysis.rules; + +import org.apache.nifi.annotation.documentation.CapabilityDescription; +import org.apache.nifi.annotation.documentation.Tags; +import org.apache.nifi.components.AllowableValue; +import org.apache.nifi.components.PropertyDescriptor; +import org.apache.nifi.components.ValidationContext; +import org.apache.nifi.components.ValidationResult; +import org.apache.nifi.flow.VersionedProcessGroup; +import org.apache.nifi.flowanalysis.AbstractFlowAnalysisRule; +import org.apache.nifi.flowanalysis.FlowAnalysisRuleContext; +import org.apache.nifi.flowanalysis.GroupAnalysisResult; +import org.apache.nifi.flowanalysis.rules.util.ConnectionViolation; +import org.apache.nifi.flowanalysis.rules.util.FlowAnalysisRuleUtils; +import org.apache.nifi.flowanalysis.rules.util.ViolationType; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +@Tags({"connection", "load", "balance", "balancing", "strategy", "compress"}) +@CapabilityDescription("This rule generates a violation when the Load Balance Strategy setting of a connection is set to a strategy which is not allowed. " + + "This rule may be useful for preventing certain strategy types or load-balancing compression. It may be implemented to assist in locating " + + "all connections which have load balancing enabled.") +public class RestrictLoadBalancing extends AbstractFlowAnalysisRule { + + static final String ALLOWED = "Allowed"; + static final String DISALLOWED = "Disallowed"; + static final AllowableValue ALLOWABLE_ALLOWED = new AllowableValue(ALLOWED, ALLOWED); + static final AllowableValue ALLOWABLE_DISALLOWED = new AllowableValue(DISALLOWED, DISALLOWED); + static final String CONFIGURE_STRATEGY_ERROR_MESSAGE = "is set to " + ALLOWED + " so at least one load balancing strategy property must be set to " + ALLOWED; + + // The following constants are derived from LoadBalanceStrategy and LoadBalanceCompression enums in the nifi-framework-api. + // If values in the API change, the corresponding values here must also be updated. + // The API is not referenced directly to avoid a dependency on the framework API. + private static final String PARTITION_BY_ATTRIBUTE = "PARTITION_BY_ATTRIBUTE"; + private static final String ROUND_ROBIN = "ROUND_ROBIN"; + private static final String SINGLE_NODE = "SINGLE_NODE"; + private static final String COMPRESS_ATTRIBUTES_ONLY = "COMPRESS_ATTRIBUTES_ONLY"; + private static final String COMPRESS_ATTRIBUTES_AND_CONTENT = "COMPRESS_ATTRIBUTES_AND_CONTENT"; + + + public static final PropertyDescriptor LOAD_BALANCING_POLICY = new PropertyDescriptor.Builder() + .name("Load Balancing Policy") + .description("Determines whether any load balance strategy is allowed. In all cases, 'Do not load balance' is valid and will not cause a violation.") + .required(true) + .allowableValues(ALLOWABLE_ALLOWED, ALLOWABLE_DISALLOWED) + .defaultValue(DISALLOWED) + .build(); + + public static final PropertyDescriptor ALLOW_PARTITION = new PropertyDescriptor.Builder() + .name("Partition By Attribute Strategy") + .description("Connections may be configured with the load balancing strategy of 'Partition by attribute'.") + .required(false) + .allowableValues(ALLOWABLE_ALLOWED, ALLOWABLE_DISALLOWED) + .defaultValue(ALLOWED) + .dependsOn(LOAD_BALANCING_POLICY, ALLOWED) + .build(); + + public static final PropertyDescriptor ALLOW_ROUND_ROBIN = new PropertyDescriptor.Builder() + .name("Round Robin Strategy") + .description("Connections may be configured with the load balancing strategy of 'Round robin'.") + .required(false) + .allowableValues(ALLOWABLE_ALLOWED, ALLOWABLE_DISALLOWED) + .defaultValue(ALLOWED) + .dependsOn(LOAD_BALANCING_POLICY, ALLOWED) + .build(); + + public static final PropertyDescriptor ALLOW_SINGLE_NODE = new PropertyDescriptor.Builder() + .name("Single Node Strategy") + .description("Connections may be configured with the load balancing strategy of 'Single node'.") + .required(false) + .allowableValues(ALLOWABLE_ALLOWED, ALLOWABLE_DISALLOWED) + .defaultValue(ALLOWED) + .dependsOn(LOAD_BALANCING_POLICY, ALLOWED) + .build(); + + public static final PropertyDescriptor ALLOW_ATTRIBUTE_COMPRESSION = new PropertyDescriptor.Builder() + .name("Compress Attributes Only") + .description("Connections which are configured with a load balancing strategy, may compress attributes.") + .required(false) + .allowableValues(ALLOWABLE_ALLOWED, ALLOWABLE_DISALLOWED) + .defaultValue(ALLOWED) + .dependsOn(LOAD_BALANCING_POLICY, ALLOWED) + .build(); + + public static final PropertyDescriptor ALLOW_CONTENT_COMPRESSION = new PropertyDescriptor.Builder() + .name("Compress Attributes and Content") + .description("Connections which are configured with a load balancing strategy, may compress both attributes and content.") + .required(false) + .allowableValues(ALLOWABLE_ALLOWED, ALLOWABLE_DISALLOWED) + .defaultValue(ALLOWED) + .dependsOn(LOAD_BALANCING_POLICY, ALLOWED) + .build(); + + private static final List PROPERTY_DESCRIPTORS = List.of( + LOAD_BALANCING_POLICY, + ALLOW_PARTITION, + ALLOW_ROUND_ROBIN, + ALLOW_SINGLE_NODE, + ALLOW_ATTRIBUTE_COMPRESSION, + ALLOW_CONTENT_COMPRESSION + ); + + @Override + protected List getSupportedPropertyDescriptors() { + return PROPERTY_DESCRIPTORS; + } + + @Override + protected Collection customValidate(ValidationContext validationContext) { + final List results = new ArrayList<>(); + + // For this flow analysis rule to be valid, load balancing must be disallowed, or at least one load balancing strategy must be allowed + if (validationContext.getProperty(LOAD_BALANCING_POLICY).getValue().equals(ALLOWED) + && !isLoadBalancingStrategyAllowed(validationContext)) { + results.add(new ValidationResult.Builder() + .subject(LOAD_BALANCING_POLICY.getName()) + .valid(false) + .explanation(LOAD_BALANCING_POLICY.getName() + " " + CONFIGURE_STRATEGY_ERROR_MESSAGE) + .build()); + } + + return results; + } + + @Override + public Collection analyzeProcessGroup(VersionedProcessGroup processGroup, FlowAnalysisRuleContext context) { + final Collection violations = new ArrayList<>(); + final boolean allowLoadBalancing = context.getProperty(LOAD_BALANCING_POLICY).getValue().equals(ALLOWED); + final boolean allowPartition = context.getProperty(ALLOW_PARTITION).getValue().equals(ALLOWED); + final boolean allowRoundRobin = context.getProperty(ALLOW_ROUND_ROBIN).getValue().equals(ALLOWED); + final boolean allowSingleNode = context.getProperty(ALLOW_SINGLE_NODE).getValue().equals(ALLOWED); + final boolean allowAttributesCompression = context.getProperty(ALLOW_ATTRIBUTE_COMPRESSION).getValue().equals(ALLOWED); + final boolean allowAttributesAndContentCompression = context.getProperty(ALLOW_CONTENT_COMPRESSION).getValue().equals(ALLOWED); + + processGroup.getConnections().forEach(connection -> { + final String strategy = connection.getLoadBalanceStrategy(); + + if (isLoadBalancingEnabled(strategy) && !allowLoadBalancing) { + violations.add(new ConnectionViolation(connection, + LoadBalanceViolationType.LOAD_BALANCE_DISALLOWED, + this.getClass().getSimpleName(), + connection.getLoadBalanceStrategy(), + context.getProperty(LOAD_BALANCING_POLICY).getValue())); + } + if (PARTITION_BY_ATTRIBUTE.equals(strategy) && !allowPartition) { + violations.add(new ConnectionViolation(connection, + LoadBalanceViolationType.PARTITION_DISALLOWED, + this.getClass().getSimpleName(), + connection.getLoadBalanceStrategy(), + context.getProperty(ALLOW_PARTITION).getValue())); + } + if (ROUND_ROBIN.equals(strategy) && !allowRoundRobin) { + violations.add(new ConnectionViolation(connection, + LoadBalanceViolationType.ROUND_ROBIN_DISALLOWED, + this.getClass().getSimpleName(), + connection.getLoadBalanceStrategy(), + context.getProperty(ALLOW_ROUND_ROBIN).getValue())); + } + if (SINGLE_NODE.equals(strategy) && !allowSingleNode) { + violations.add(new ConnectionViolation(connection, + LoadBalanceViolationType.SINGLE_NODE_DISALLOWED, + this.getClass().getSimpleName(), + connection.getLoadBalanceStrategy(), + context.getProperty(ALLOW_SINGLE_NODE).getValue())); + } + + // Evaluate compression only if one of the load balance strategies is specified + if (isLoadBalancingEnabled(strategy)) { + String compression = connection.getLoadBalanceCompression(); + if (COMPRESS_ATTRIBUTES_ONLY.equals(compression) && !allowAttributesCompression) { + violations.add(new ConnectionViolation(connection, + LoadBalanceViolationType.ATTRIBUTE_COMPRESSION_DISALLOWED, + this.getClass().getSimpleName(), + connection.getLoadBalanceCompression(), + context.getProperty(ALLOW_ATTRIBUTE_COMPRESSION).getValue())); + } + if (COMPRESS_ATTRIBUTES_AND_CONTENT.equals(compression) && !allowAttributesAndContentCompression) { + violations.add(new ConnectionViolation(connection, + LoadBalanceViolationType.CONTENT_COMPRESSION_DISALLOWED, + this.getClass().getSimpleName(), + connection.getLoadBalanceCompression(), + context.getProperty(ALLOW_CONTENT_COMPRESSION).getValue())); + } + } + }); + + return FlowAnalysisRuleUtils.convertToGroupAnalysisResults(processGroup, violations); + } + + private boolean isLoadBalancingStrategyAllowed(ValidationContext validationContext) { + return validationContext.getProperty(ALLOW_PARTITION).getValue().equals(ALLOWED) + || validationContext.getProperty(ALLOW_ROUND_ROBIN).getValue().equals(ALLOWED) + || validationContext.getProperty(ALLOW_SINGLE_NODE).getValue().equals(ALLOWED); + } + + private boolean isLoadBalancingEnabled(String strategy) { + return PARTITION_BY_ATTRIBUTE.equals(strategy) + || ROUND_ROBIN.equals(strategy) + || SINGLE_NODE.equals(strategy); + } + + private enum LoadBalanceViolationType implements ViolationType { + + LOAD_BALANCE_DISALLOWED("NoLoadBalanceNotAllowed", "Load Balance Strategy", "is disallowed because \"" + LOAD_BALANCING_POLICY.getName() + "\" is set to "), + PARTITION_DISALLOWED("PartitionByAttributeNotAllowed", "Load Balance Strategy", "is disallowed because \"" + ALLOW_PARTITION.getName() + "\" is set to "), + ROUND_ROBIN_DISALLOWED("RoundRobinNotAllowed", "Load Balance Strategy", "is disallowed because \"" + ALLOW_ROUND_ROBIN.getName() + "\" is set to "), + SINGLE_NODE_DISALLOWED("SingleNodeNotAllowed", "Load Balance Strategy", "is disallowed because \"" + ALLOW_SINGLE_NODE.getName() + "\" is set to "), + ATTRIBUTE_COMPRESSION_DISALLOWED("AttributeCompressionNotAllowed", "Load Balance Compression", "is disallowed because \"" + ALLOW_ATTRIBUTE_COMPRESSION.getName() + "\" is set to "), + CONTENT_COMPRESSION_DISALLOWED("ContentCompressionNotAllowed", "Load Balance Compression", "is disallowed because \"" + ALLOW_CONTENT_COMPRESSION.getName() + "\" is set to "); + + private final String id; + private final String configurationItem; + private final String violationMessage; + + LoadBalanceViolationType(String id, String configurationItem, String violationMessage) { + this.id = id; + this.configurationItem = configurationItem; + this.violationMessage = violationMessage; + } + + @Override + public String getId() { + return this.id; + } + + @Override + public String getConfigurationItem() { + return this.configurationItem; + } + + @Override + public String getViolationMessage() { + return this.violationMessage; + } + } +} diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/resources/META-INF/services/org.apache.nifi.flowanalysis.FlowAnalysisRule b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/resources/META-INF/services/org.apache.nifi.flowanalysis.FlowAnalysisRule index f5f1df7eb4b5..077d954ca308 100644 --- a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/resources/META-INF/services/org.apache.nifi.flowanalysis.FlowAnalysisRule +++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/main/resources/META-INF/services/org.apache.nifi.flowanalysis.FlowAnalysisRule @@ -16,3 +16,4 @@ org.apache.nifi.flowanalysis.rules.DisallowComponentType org.apache.nifi.flowanalysis.rules.RestrictBackpressureSettings org.apache.nifi.flowanalysis.rules.RestrictFlowFileExpiration +org.apache.nifi.flowanalysis.rules.RestrictLoadBalancing diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/AbstractFlowAnalaysisRuleTest.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/AbstractFlowAnalaysisRuleTest.java index 04b2d38acbd5..5ada34aa608e 100644 --- a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/AbstractFlowAnalaysisRuleTest.java +++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/AbstractFlowAnalaysisRuleTest.java @@ -85,6 +85,7 @@ private VersionedProcessGroup getProcessGroup(String flowDefinition) throws Exce protected void testAnalyzeProcessGroup(String flowDefinition, List expected) throws Exception { final Collection actual = rule.analyzeProcessGroup(getProcessGroup(flowDefinition), flowAnalysisRuleContext); - assertIterableEquals(expected, actual.stream().map(r -> r.getComponent().get().getInstanceIdentifier()).sorted().toList()); + assertIterableEquals(expected.stream().sorted().toList(), + actual.stream().map(r -> r.getComponent().get().getInstanceIdentifier()).sorted().toList()); } } diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/RestrictLoadBalancingTest.java b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/RestrictLoadBalancingTest.java new file mode 100644 index 000000000000..ee8ee1b5863d --- /dev/null +++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/java/org/apache/nifi/flowanalysis/rules/RestrictLoadBalancingTest.java @@ -0,0 +1,171 @@ +/* + * 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. + */ +package org.apache.nifi.flowanalysis.rules; + +import org.apache.nifi.components.ValidationResult; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class RestrictLoadBalancingTest extends AbstractFlowAnalaysisRuleTest { + // The flow consists of six upstream GenerateFlowFile processors each having one connection to an UpdateAttribute processor + // The UUID of the upstream processor is specified in the following variables which describe the properties of the connection + // The sixth upstream processor has a connection with 'Do not load balance'; it can never generate a violation + private static final String UPSTREAM_PROCESSOR_PARTITION_BY_ATTRIBUTE_UUID = "2f3f4270-9703-331a-659c-1c583972547b"; + private static final String UPSTREAM_PROCESSOR_ROUND_ROBIN_UUID = "930d2d12-fefe-39d2-4884-fb9d9330d892"; + private static final String UPSTREAM_PROCESSOR_SINGLE_NODE_UUID = "aa0e758e-0194-1000-a703-6b124a357ce2"; + private static final String UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_UUID = "78a971d6-3015-3fc1-17c5-68b7be982aa2"; + private static final String UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_AND_CONTENT_UUID = "8792e315-8937-300c-ccbd-3130ceca40fc"; + + @Override + protected RestrictLoadBalancing initializeRule() { + return new RestrictLoadBalancing(); + } + + @BeforeEach + @Override + public void setup() { + super.setup(); + // Non-default setting, but appropriate for unit tests + setProperty(RestrictLoadBalancing.LOAD_BALANCING_POLICY, RestrictLoadBalancing.ALLOWED); + // Default settings + setProperty(RestrictLoadBalancing.ALLOW_PARTITION, RestrictLoadBalancing.ALLOWED); + setProperty(RestrictLoadBalancing.ALLOW_ROUND_ROBIN, RestrictLoadBalancing.ALLOWED); + setProperty(RestrictLoadBalancing.ALLOW_SINGLE_NODE, RestrictLoadBalancing.ALLOWED); + setProperty(RestrictLoadBalancing.ALLOW_ATTRIBUTE_COMPRESSION, RestrictLoadBalancing.ALLOWED); + setProperty(RestrictLoadBalancing.ALLOW_CONTENT_COMPRESSION, RestrictLoadBalancing.ALLOWED); + } + + @Test + public void testBadConfiguration() { + // Set all load balancing strategies to 'Disallowed' + setProperty(RestrictLoadBalancing.ALLOW_PARTITION, RestrictLoadBalancing.DISALLOWED); + setProperty(RestrictLoadBalancing.ALLOW_ROUND_ROBIN, RestrictLoadBalancing.DISALLOWED); + setProperty(RestrictLoadBalancing.ALLOW_SINGLE_NODE, RestrictLoadBalancing.DISALLOWED); + ArrayList validationErrors = new ArrayList<>(rule.customValidate(validationContext)); + assertEquals(1, validationErrors.size()); + assertTrue(validationErrors.getFirst().getExplanation().contains(RestrictLoadBalancing.CONFIGURE_STRATEGY_ERROR_MESSAGE)); + } + + @Test + public void testGoodConfiguration() { + // Set load balancing policy to disallowed while strategies and compression are also allowed + setProperty(RestrictLoadBalancing.LOAD_BALANCING_POLICY, RestrictLoadBalancing.DISALLOWED); + ArrayList validationErrors = new ArrayList<>(rule.customValidate(validationContext)); + assertEquals(0, validationErrors.size()); + } + + @Test + public void testNoViolations() throws Exception { + // Using unit test defaults, all load balancing configurations are allowed + testAnalyzeProcessGroup( + "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json", + List.of() + ); + } + + @Test + public void testViolationsLoadBalancePolicy() throws Exception { + setProperty(RestrictLoadBalancing.LOAD_BALANCING_POLICY, RestrictLoadBalancing.DISALLOWED); + // Using unit test defaults, all load balancing configurations are allowed + testAnalyzeProcessGroup( + "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json", + List.of( + UPSTREAM_PROCESSOR_PARTITION_BY_ATTRIBUTE_UUID, + UPSTREAM_PROCESSOR_ROUND_ROBIN_UUID, + UPSTREAM_PROCESSOR_SINGLE_NODE_UUID, + UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_UUID, + UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_AND_CONTENT_UUID + ) + ); + } + + @Test + public void testViolationRoundRobin() throws Exception { + setProperty(RestrictLoadBalancing.ALLOW_ROUND_ROBIN, RestrictLoadBalancing.DISALLOWED); + testAnalyzeProcessGroup( + "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json", + List.of( + // Compression connections are also configured with Round robin strategy + UPSTREAM_PROCESSOR_ROUND_ROBIN_UUID, + UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_UUID, + UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_AND_CONTENT_UUID + ) + ); + } + + @Test + public void testViolationSingleNode() throws Exception { + setProperty(RestrictLoadBalancing.ALLOW_SINGLE_NODE, RestrictLoadBalancing.DISALLOWED); + testAnalyzeProcessGroup( + "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json", + List.of( + UPSTREAM_PROCESSOR_SINGLE_NODE_UUID + ) + ); + } + + @Test + public void testViolationPartition() throws Exception { + setProperty(RestrictLoadBalancing.ALLOW_PARTITION, RestrictLoadBalancing.DISALLOWED); + testAnalyzeProcessGroup( + "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json", + List.of( + UPSTREAM_PROCESSOR_PARTITION_BY_ATTRIBUTE_UUID + ) + ); + } + + @Test + public void testViolationAttributeCompression() throws Exception { + setProperty(RestrictLoadBalancing.ALLOW_ATTRIBUTE_COMPRESSION, RestrictLoadBalancing.DISALLOWED); + setProperty(RestrictLoadBalancing.ALLOW_CONTENT_COMPRESSION, RestrictLoadBalancing.DISALLOWED); + testAnalyzeProcessGroup( + "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json", + List.of( + UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_UUID, + UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_AND_CONTENT_UUID + ) + ); + } + + @Test + public void testViolationContentCompression() throws Exception { + setProperty(RestrictLoadBalancing.ALLOW_CONTENT_COMPRESSION, RestrictLoadBalancing.DISALLOWED); + testAnalyzeProcessGroup( + "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json", + List.of( + UPSTREAM_PROCESSOR_RR_COMPRESS_ATTRIBUTES_AND_CONTENT_UUID + ) + ); + } + + @Test + public void testNoViolationContentCompression() throws Exception { + setProperty(RestrictLoadBalancing.ALLOW_ATTRIBUTE_COMPRESSION, RestrictLoadBalancing.ALLOWED); + setProperty(RestrictLoadBalancing.ALLOW_CONTENT_COMPRESSION, RestrictLoadBalancing.ALLOWED); + testAnalyzeProcessGroup( + "src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json", + List.of() + ); + } +} \ No newline at end of file diff --git a/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json new file mode 100644 index 000000000000..5e78e49cdc4d --- /dev/null +++ b/nifi-extension-bundles/nifi-standard-bundle/nifi-standard-rules/src/test/resources/RestrictLoadBalancing/RestrictLoadBalancing.json @@ -0,0 +1 @@ +{"flowContents":{"identifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29","instanceIdentifier":"d1fd0a0b-0194-1000-7312-766249d205c0","name":"RestrictLoadBalancing","comments":"","position":{"x":-176.0,"y":-160.0},"processGroups":[],"remoteProcessGroups":[],"processors":[{"identifier":"d2521342-0194-1000-350f-654464c43ba6","instanceIdentifier":"8f321b49-577c-38d5-ffe0-6ee325ddf030","name":"GenerateFlowFile","comments":"","position":{"x":-368.0,"y":-568.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.2.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"${random():mod(6)} B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"10","Unique FlowFiles":"true","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"d252f44b-0194-1000-d380-6c9cb17885ec","instanceIdentifier":"930d2d12-fefe-39d2-4884-fb9d9330d892","name":"GenerateFlowFile","comments":"","position":{"x":-368.0,"y":-288.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.2.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"${random():mod(6)} B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"10","Unique FlowFiles":"true","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"d252814b-0194-1000-c644-753fe971fef5","instanceIdentifier":"2f3f4270-9703-331a-659c-1c583972547b","name":"GenerateFlowFile","comments":"","position":{"x":-368.0,"y":-432.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.2.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"${random():mod(6)} B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"10","Unique FlowFiles":"true","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"41493fc8-8750-31fd-a4d4-e97a139ff400","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c","name":"UpdateAttribute","comments":"","position":{"x":416.0,"y":-224.0},"type":"org.apache.nifi.processors.attributes.UpdateAttribute","bundle":{"group":"org.apache.nifi","artifact":"nifi-update-attribute-nar","version":"2.2.0-SNAPSHOT"},"properties":{"Delete Attributes Expression":null,"Store State":"Do not store state","canonical-value-lookup-cache-size":"100","Stateful Variables Initial Value":null},"propertyDescriptors":{"Delete Attributes Expression":{"name":"Delete Attributes Expression","displayName":"Delete Attributes Expression","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Store State":{"name":"Store State","displayName":"Store State","identifiesControllerService":false,"sensitive":false,"dynamic":false},"canonical-value-lookup-cache-size":{"name":"canonical-value-lookup-cache-size","displayName":"Cache Value Lookup Cache Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Stateful Variables Initial Value":{"name":"Stateful Variables Initial Value","displayName":"Stateful Variables Initial Value","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"0 sec","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":25,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"901df113-9ade-38f0-96bd-92436b5881b7","instanceIdentifier":"aa0e758e-0194-1000-a703-6b124a357ce2","name":"GenerateFlowFile","comments":"","position":{"x":-368.0,"y":-152.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.2.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"${random():mod(6)} B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"10","Unique FlowFiles":"true","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"d23d54f3-0194-1000-0049-920ac8d1f9ca","instanceIdentifier":"78a971d6-3015-3fc1-17c5-68b7be982aa2","name":"GenerateFlowFile","comments":"","position":{"x":-368.0,"y":-16.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.2.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"${random():mod(6)} B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"10","Unique FlowFiles":"true","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"d23d5b3e-0194-1000-a180-c3c4435e457d","instanceIdentifier":"8792e315-8937-300c-ccbd-3130ceca40fc","name":"GenerateFlowFile","comments":"","position":{"x":-368.0,"y":120.0},"type":"org.apache.nifi.processors.standard.GenerateFlowFile","bundle":{"group":"org.apache.nifi","artifact":"nifi-standard-nar","version":"2.2.0-SNAPSHOT"},"properties":{"character-set":"UTF-8","File Size":"${random():mod(6)} B","mime-type":null,"generate-ff-custom-text":null,"Batch Size":"10","Unique FlowFiles":"true","Data Format":"Text"},"propertyDescriptors":{"character-set":{"name":"character-set","displayName":"Character Set","identifiesControllerService":false,"sensitive":false,"dynamic":false},"File Size":{"name":"File Size","displayName":"File Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"mime-type":{"name":"mime-type","displayName":"Mime Type","identifiesControllerService":false,"sensitive":false,"dynamic":false},"generate-ff-custom-text":{"name":"generate-ff-custom-text","displayName":"Custom Text","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Batch Size":{"name":"Batch Size","displayName":"Batch Size","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Unique FlowFiles":{"name":"Unique FlowFiles","displayName":"Unique FlowFiles","identifiesControllerService":false,"sensitive":false,"dynamic":false},"Data Format":{"name":"Data Format","displayName":"Data Format","identifiesControllerService":false,"sensitive":false,"dynamic":false}},"style":{},"schedulingPeriod":"1 min","schedulingStrategy":"TIMER_DRIVEN","executionNode":"ALL","penaltyDuration":"30 sec","yieldDuration":"1 sec","bulletinLevel":"WARN","runDurationMillis":0,"concurrentlySchedulableTaskCount":1,"autoTerminatedRelationships":[],"scheduledState":"ENABLED","retryCount":10,"retriedRelationships":[],"backoffMechanism":"PENALIZE_FLOWFILE","maxBackoffPeriod":"10 mins","componentType":"PROCESSOR","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"}],"inputPorts":[],"outputPorts":[],"connections":[{"identifier":"f4bb6f79-eb85-32ee-8a1d-574014b36523","instanceIdentifier":"d25342d7-0194-1000-0c3f-8253ceeb017f","name":"round-robin","source":{"id":"d252f44b-0194-1000-d380-6c9cb17885ec","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"GenerateFlowFile","comments":"","instanceIdentifier":"930d2d12-fefe-39d2-4884-fb9d9330d892"},"destination":{"id":"41493fc8-8750-31fd-a4d4-e97a139ff400","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"UpdateAttribute","comments":"","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"ROUND_ROBIN","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"2928bd29-f2d9-3e91-93a2-96452008c6ce","instanceIdentifier":"d2525cb9-0194-1000-1bb2-8d5434847677","name":"do-not-load-balance","source":{"id":"d2521342-0194-1000-350f-654464c43ba6","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"GenerateFlowFile","comments":"","instanceIdentifier":"8f321b49-577c-38d5-ffe0-6ee325ddf030"},"destination":{"id":"41493fc8-8750-31fd-a4d4-e97a139ff400","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"UpdateAttribute","comments":"","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"DO_NOT_LOAD_BALANCE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"65090306-dfe2-3580-b920-d55503b4f4dd","instanceIdentifier":"d23e3c2b-0194-1000-b467-6ea5edfc3ac3","name":"robin-robin_compress-attributes","source":{"id":"d23d54f3-0194-1000-0049-920ac8d1f9ca","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"GenerateFlowFile","comments":"","instanceIdentifier":"78a971d6-3015-3fc1-17c5-68b7be982aa2"},"destination":{"id":"41493fc8-8750-31fd-a4d4-e97a139ff400","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"UpdateAttribute","comments":"","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"ROUND_ROBIN","partitioningAttribute":"","loadBalanceCompression":"COMPRESS_ATTRIBUTES_ONLY","componentType":"CONNECTION","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"215fdff4-0f21-31dc-919a-3da83df392f2","instanceIdentifier":"d23e8d64-0194-1000-9edb-e0934db20d0e","name":"round-robin_compress-attributes-and-content","source":{"id":"d23d5b3e-0194-1000-a180-c3c4435e457d","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"GenerateFlowFile","comments":"","instanceIdentifier":"8792e315-8937-300c-ccbd-3130ceca40fc"},"destination":{"id":"41493fc8-8750-31fd-a4d4-e97a139ff400","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"UpdateAttribute","comments":"","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"ROUND_ROBIN","partitioningAttribute":"","loadBalanceCompression":"COMPRESS_ATTRIBUTES_AND_CONTENT","componentType":"CONNECTION","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"c24e03a8-8903-336d-ba48-92b807af5291","instanceIdentifier":"d252d485-0194-1000-3fcf-48e42aa3ef8c","name":"partition-by-attribute","source":{"id":"d252814b-0194-1000-c644-753fe971fef5","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"GenerateFlowFile","comments":"","instanceIdentifier":"2f3f4270-9703-331a-659c-1c583972547b"},"destination":{"id":"41493fc8-8750-31fd-a4d4-e97a139ff400","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"UpdateAttribute","comments":"","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"PARTITION_BY_ATTRIBUTE","partitioningAttribute":"foo","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"},{"identifier":"e28059a2-46e1-3b4a-afa3-e1c65419662e","instanceIdentifier":"d1f99f32-0194-1000-a03c-f706654810a5","name":"single-node","source":{"id":"901df113-9ade-38f0-96bd-92436b5881b7","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"GenerateFlowFile","comments":"","instanceIdentifier":"aa0e758e-0194-1000-a703-6b124a357ce2"},"destination":{"id":"41493fc8-8750-31fd-a4d4-e97a139ff400","type":"PROCESSOR","groupId":"39ca530c-9fd5-359f-92be-f1dcc2712e29","name":"UpdateAttribute","comments":"","instanceIdentifier":"51de6128-0194-1000-8056-b4fd0ea3a52c"},"labelIndex":0,"zIndex":0,"selectedRelationships":["success"],"backPressureObjectThreshold":10000,"backPressureDataSizeThreshold":"1 GB","flowFileExpiration":"0 sec","prioritizers":[],"bends":[],"loadBalanceStrategy":"SINGLE_NODE","partitioningAttribute":"","loadBalanceCompression":"DO_NOT_COMPRESS","componentType":"CONNECTION","groupIdentifier":"39ca530c-9fd5-359f-92be-f1dcc2712e29"}],"labels":[],"funnels":[],"controllerServices":[],"defaultFlowFileExpiration":"0 sec","defaultBackPressureObjectThreshold":10000,"defaultBackPressureDataSizeThreshold":"1 GB","scheduledState":"ENABLED","executionEngine":"INHERITED","maxConcurrentTasks":1,"statelessFlowTimeout":"1 min","flowFileConcurrency":"UNBOUNDED","flowFileOutboundPolicy":"STREAM_WHEN_AVAILABLE","componentType":"PROCESS_GROUP"},"externalControllerServices":{},"parameterContexts":{},"flowEncodingVersion":"1.0","parameterProviders":{},"latest":false} \ No newline at end of file