NIFI-6726: Updated fingerprint logic to ensure that Controller Services are fingerprinted when housed within a ProcessGroup

This closes #3775.

Signed-off-by: Bryan Bende <bbende@apache.org>
This commit is contained in:
Mark Payne 2019-09-30 10:21:31 -04:00 committed by Bryan Bende
parent 5562c587fe
commit 6dccdd586e
No known key found for this signature in database
GPG Key ID: A0DDA9ED50711C39
3 changed files with 76 additions and 4 deletions

View File

@ -238,7 +238,7 @@ public class FingerprintFactory {
// root group
final Element rootGroupElem = (Element) DomUtils.getChildNodesByTagName(flowControllerElem, "rootGroup").item(0);
addProcessGroupFingerprint(builder, rootGroupElem, controller);
addProcessGroupFingerprint(builder, rootGroupElem, encodingVersion);
final Element controllerServicesElem = DomUtils.getChild(flowControllerElem, "controllerServices");
if (controllerServicesElem != null) {
@ -354,7 +354,7 @@ public class FingerprintFactory {
return builder;
}
private StringBuilder addProcessGroupFingerprint(final StringBuilder builder, final Element processGroupElem, final FlowController controller) throws FingerprintException {
StringBuilder addProcessGroupFingerprint(final StringBuilder builder, final Element processGroupElem, final FlowEncodingVersion encodingVersion) throws FingerprintException {
// id
appendFirstValue(builder, DomUtils.getChildNodesByTagName(processGroupElem, "id"));
appendFirstValue(builder, DomUtils.getChildNodesByTagName(processGroupElem, "versionedComponentId"));
@ -372,7 +372,7 @@ public class FingerprintFactory {
// processors
final List<Element> processorElems = DomUtils.getChildElementsByTagName(processGroupElem, "processor");
Collections.sort(processorElems, getIdsComparator());
processorElems.sort(getIdsComparator());
for (final Element processorElem : processorElems) {
addFlowFileProcessorFingerprint(builder, processorElem);
}
@ -402,7 +402,7 @@ public class FingerprintFactory {
final NodeList nestedProcessGroupElems = DomUtils.getChildNodesByTagName(processGroupElem, "processGroup");
final List<Element> sortedNestedProcessGroupElems = sortElements(nestedProcessGroupElems, getIdsComparator());
for (final Element nestedProcessGroupElem : sortedNestedProcessGroupElems) {
addProcessGroupFingerprint(builder, nestedProcessGroupElem, controller);
addProcessGroupFingerprint(builder, nestedProcessGroupElem, encodingVersion);
}
// remote process groups
@ -426,6 +426,13 @@ public class FingerprintFactory {
addFunnelFingerprint(builder, funnelElem);
}
final NodeList controllerServiceElems = DomUtils.getChildNodesByTagName(processGroupElem, "controllerService");
final List<Element> sortedControllerServiceElems = sortElements(controllerServiceElems, getIdsComparator());
for (final Element controllerServiceElem : sortedControllerServiceElems) {
final ControllerServiceDTO dto = FlowFromDOMFactory.getControllerService(controllerServiceElem, encryptor, encodingVersion);
addControllerServiceFingerprint(builder, dto);
}
// add variables
final NodeList variableElems = DomUtils.getChildNodesByTagName(processGroupElem, "variable");
final List<Element> sortedVarList = sortElements(variableElems, getVariableNameComparator());

View File

@ -20,6 +20,7 @@ import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.connectable.Position;
import org.apache.nifi.controller.ScheduledState;
import org.apache.nifi.controller.serialization.FlowEncodingVersion;
import org.apache.nifi.controller.serialization.FlowSerializer;
import org.apache.nifi.controller.serialization.ScheduledStateLookup;
import org.apache.nifi.controller.serialization.StandardFlowSerializer;
@ -40,8 +41,10 @@ import org.xml.sax.SAXParseException;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Collections;
@ -323,4 +326,29 @@ public class FingerprintFactoryTest {
final Element componentElement = (Element) rootElement.getElementsByTagName("inputPort").item(0);
assertEquals(expected, fingerprint("addRemoteGroupPortFingerprint", Element.class, componentElement));
}
@Test
public void testControllerServicesIncludedInGroupFingerprint() throws ParserConfigurationException, IOException, SAXException {
final DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
final DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
final Document document = docBuilder.parse(new File("src/test/resources/nifi/fingerprint/group-with-controller-services.xml"));
final Element processGroup = document.getDocumentElement();
final StringBuilder sb = new StringBuilder();
fingerprinter.addProcessGroupFingerprint(sb, processGroup, new FlowEncodingVersion(1, 0));
final String fingerprint = sb.toString();
final String[] criticalFingerprintValues = new String[] {
"1234",
"s1", "service1", "prop1", "value1", "org.apache.nifi.services.FingerprintControllerService",
"s2", "service2", "another property", "another value", "org.apache.nifi.services.AnotherService",
};
for (final String criticalValue : criticalFingerprintValues) {
assertTrue("Fingerprint did not contain '" + criticalValue + "'", fingerprint.contains(criticalValue));
}
// Ensure that 's1' comes before 's2' in the fingerprint
assertTrue(fingerprint.indexOf("FingerprintControllerService") < fingerprint.indexOf("AnotherService"));
}
}

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!--
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.
-->
<processGroup>
<id>1234</id>
<name>pg1</name>
<controllerService>
<id>s1</id>
<name>service1</name>
<class>org.apache.nifi.services.FingerprintControllerService</class>
<property>
<name>prop1</name>
<value>value1</value>
</property>
</controllerService>
<controllerService>
<id>s2</id>
<name>service2</name>
<class>org.apache.nifi.services.AnotherService</class>
<property>
<name>another property</name>
<value>another value</value>
</property>
</controllerService>
</processGroup>